/* $Id: VBoxMPWddm.cpp $ */
/** @file
 * VBox WDDM Miniport driver
 */

 /*
  * Copyright (C) 2011-2020 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
  * available from http://www.virtualbox.org. This file is free software;
  * you can redistribute it and/or modify it under the terms of the GNU
  * General Public License (GPL) as published by the Free Software
  * Foundation, in version 2 as it comes in the "COPYING" file of the
  * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
  * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
  */
#include <stdio.h>

#include "ntifs.h"
#include "VBoxMPWddm.h"
#include "VBoxMPVidPn.h"
#include "VBoxMPMem.h"
#include "VBoxMPMisc.h"
#include "CommonDef.h"
#include "VBoxVideoVBE.h"
#include "trace.h"
#include "asm.h"
#include "VBoxMPRegistry.h"
#include "VBoxMPVModes.h"
#include "VBoxVideo.h"
#include "VBoxVideoGuest.h"
#include "WddmPortIo.h"
#include "gallium/VBoxMPGaWddm.h"
#include "common/VBoxMPUtils.h"
#include "gallium/SvgaHw.h"
#include "gallium/VBoxMPGaExt.h"

BOOLEAN g_VBoxDisplayOnly = FALSE;
vboxWinVersion_t g_windowsVersion;
volatile DWORD g_RefreshRate = VBOXWDDM_DEFAULT_REFRESH_RATE;
void* mappedSysIoPort;

#define VBOXWDDM_MEMTAG 'MDBV'

/* according to the DT of qemu arm machine,
 * FW_CFG memory starts from 0x09020000.
 */
#define FW_CFG_BASE_PHYS_ADDR 0x09020000
#define FW_CFG_EXT_ADDR (FW_CFG_BASE_PHYS_ADDR + 24)
#define FW_CFG_EXT_REG_VMSVGA 0
#define EXT_VMSVGA_3D 0x1

#define MAX_HW_CURSOR_WIDTH 128
#define MAX_HW_CURSOR_HEIGHT 128

extern "C"
static BOOL qemu_support_3d(void)
{
    UINT64 *cfg_ptr, cfg_value;
    PHYSICAL_ADDRESS io_addr;

    io_addr.QuadPart = FW_CFG_EXT_ADDR;
    cfg_ptr = (UINT64 *)MmMapIoSpace(io_addr, 8, MmNonCached);
    if (cfg_ptr == NULL) {
        LOGE("Failed to map fw cfg io space\n");
        return FALSE;
    }

    //fw_cfg_ext is big endianness
    WRITE_REGISTER_ULONG64(cfg_ptr, RtlUlonglongByteSwap((ULONGLONG)FW_CFG_EXT_REG_VMSVGA));
    cfg_value = RtlUlonglongByteSwap((ULONGLONG)READ_REGISTER_ULONG64(cfg_ptr));

    MmUnmapIoSpace((PVOID)cfg_ptr, 8);

    return !!(cfg_value & EXT_VMSVGA_3D);
}

PVOID vboxWddmMemAlloc(SIZE_T cbSize)
{
    return ExAllocatePool2(POOL_FLAG_NON_PAGED, cbSize, SVGA_MEMTAG);
}

PVOID vboxWddmMemAllocZero(SIZE_T cbSize)
{
    PVOID pvMem = vboxWddmMemAlloc(cbSize);
    if (pvMem)
        memset(pvMem, 0, cbSize);
    return pvMem;
}

VOID vboxWddmMemFree(PVOID pvMem)
{
    ExFreePoolWithTag(pvMem, SVGA_MEMTAG);
}

NTSTATUS APIENTRY DxgkDdiDestroyContext(
    CONST HANDLE hContext)
{
    LOGI("==> hContext(0x%p)\n", hContext);

    PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)hContext;
    PVBOXMP_DEVEXT pDevExt = pContext->pDevice->pAdapter;
    NTSTATUS Status = STATUS_SUCCESS;

    switch(pContext->enmType)
    {
        case VBOXWDDM_CONTEXT_TYPE_GA_3D:
        {
            Status = GaContextDestroy(pDevExt->pGa, pContext);
            break;
        }
#ifdef VBOX_WITH_VMSVGA3D_DX
        case VBOXWDDM_CONTEXT_TYPE_VMSVGA_D3D:
        {
            Status = GaContextDestroy(pDevExt->pGa, pContext);
            break;
        }
#endif
        default:
            LOGI("Not implement context type(%d)\n", pContext->enmType);
            break;
    }

    if (NT_SUCCESS(Status)) {
        vboxWddmMemFree(pContext);
    }

    LOGI("<== hContext(0x%p)\n", hContext);

    return Status;
}

NTSTATUS APIENTRY DxgkDdiCreateContext(
    CONST HANDLE hDevice,
    DXGKARG_CREATECONTEXT *pCreateContext)
{
    /* DxgkDdiCreateContext should be made pageable */
    PAGED_CODE();

    LOGI("==> hDevice(0x%p), pCreateContext(0x%p)\n", hDevice, pCreateContext);

    if (pCreateContext->NodeOrdinal >= VBOXWDDM_NUM_NODES)
    {
        LOGI("Invalid NodeOrdinal (%d), expected to be less that (%d)\n",
             pCreateContext->NodeOrdinal, VBOXWDDM_NUM_NODES);
        return STATUS_INVALID_PARAMETER;
    }

    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_DEVICE pDevice = (PVBOXWDDM_DEVICE)hDevice;
    PVBOXMP_DEVEXT pDevExt = pDevice->pAdapter;
    PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)vboxWddmMemAllocZero(sizeof(VBOXWDDM_CONTEXT));

    if (!pContext)
    {
        LOGE("DxgkDdiCreateContext 9 \n");
        return STATUS_NO_MEMORY;
    }

    LOGI("DxgkDdiCreateContext 1 \n");
    pContext->pDevice = pDevice;
    pContext->hContext = pCreateContext->hContext;
    pContext->EngineAffinity = pCreateContext->EngineAffinity;
    pContext->NodeOrdinal = pCreateContext->NodeOrdinal;
    if (pCreateContext->Flags.SystemContext || pCreateContext->PrivateDriverDataSize == 0)
    {
        LOGI("DxgkDdiCreateContext 2 date:%p, size:%d\n",
             pCreateContext->pPrivateDriverData, pCreateContext->PrivateDriverDataSize);
        pContext->enmType = VBOXWDDM_CONTEXT_TYPE_SYSTEM;
    }
    else
    {
        LOGI("DxgkDdiCreateContext 5 \n");
        if (pCreateContext->PrivateDriverDataSize == sizeof(VBOXWDDM_CREATECONTEXT_INFO))
        {
            PVBOXWDDM_CREATECONTEXT_INFO pInfo = (PVBOXWDDM_CREATECONTEXT_INFO)pCreateContext->pPrivateDriverData;
            LOGI("DxgkDdiCreateContext 6 type=%d\n", pInfo->enmType);
            switch (pInfo->enmType)
            {
                case VBOXWDDM_CONTEXT_TYPE_GA_3D:
                {
                    LOGI("create context VBOXWDDM_CONTEXT_TYPE_GA_3D\n");
                    pContext->enmType = VBOXWDDM_CONTEXT_TYPE_GA_3D;
                    Status = GaContextCreate(pDevExt->pGa, pInfo, pContext);
                    break;
                }
#ifdef VBOX_WITH_VMSVGA3D_DX
                case VBOXWDDM_CONTEXT_TYPE_VMSVGA_D3D:
                {
                    /* VMSVGA_D3D context type shares some code with GA_3D, because both work with VMSVGA GPU. */
                    pContext->enmType = VBOXWDDM_CONTEXT_TYPE_VMSVGA_D3D;
                    Status = GaContextCreate(pDevExt->pGa, pInfo, pContext);
                    break;
                }
#endif
                default:
                {
                    LOGI("unsupported context type %d\n", pInfo->enmType);
                    Status = STATUS_INVALID_PARAMETER;
                    break;
                }
            }
        }
    }

    if (Status == STATUS_SUCCESS)
    {
        LOGI("DxgkDdiCreateContext 7 \n");
        pCreateContext->hContext = pContext;
        pCreateContext->ContextInfo.DmaBufferSize = VBOXWDDM_C_DMA_BUFFER_SIZE;
        pCreateContext->ContextInfo.DmaBufferSegmentSet = 0;
        pCreateContext->ContextInfo.DmaBufferPrivateDataSize = VBOXWDDM_C_DMA_PRIVATEDATA_SIZE;
        pCreateContext->ContextInfo.AllocationListSize = VBOXWDDM_C_ALLOC_LIST_SIZE;
        pCreateContext->ContextInfo.PatchLocationListSize = VBOXWDDM_C_PATH_LOCATION_LIST_SIZE;
    }
    else
    {
        LOGI("DxgkDdiCreateContext 8 \n");
        vboxWddmMemFree(pContext);
    }

    LOGI("<== hDevice(0x%p), status=%d \n", hDevice, Status);

    return Status;
}

NTSTATUS APIENTRY DxgkDdiDestroyDevice(
    CONST HANDLE hDevice)
{
    /* DxgkDdiDestroyDevice should be made pageable. */
    PAGED_CODE();

    LOGI("==> hDevice(0x%p)\n", hDevice);
    PVBOXWDDM_DEVICE pDevice = (PVBOXWDDM_DEVICE)hDevice;
    PVBOXMP_DEVEXT pDevExt = pDevice->pAdapter;
    if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA)
    {
        GaDeviceDestroy(pDevExt->pGa, pDevice);
    }

    vboxWddmMemFree(hDevice);

    LOGI("<==\n");

    return STATUS_SUCCESS;
}

NTSTATUS APIENTRY DxgkDdiCreateDevice(
    CONST HANDLE hAdapter,
    DXGKARG_CREATEDEVICE* pCreateDevice)
{
    /* DxgkDdiCreateDevice should be made pageable. */
    PAGED_CODE();

    UINT SystemDevice = pCreateDevice->Flags.SystemDevice;
    UINT GdiDevice = pCreateDevice->Flags.GdiDevice;
    UINT vDXGK_DEVICE_RESERVED0 = pCreateDevice->Flags.DXGK_DEVICE_RESERVED0;

    LOGI("==> context(0x%p)\n", hAdapter);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    PVBOXWDDM_DEVICE pDevice = (PVBOXWDDM_DEVICE)vboxWddmMemAllocZero(sizeof(VBOXWDDM_DEVICE));
    if (!pDevice)
    {
        LOGF("vboxWddmMemAllocZero failed for WDDM device structure\n");
        return STATUS_NO_MEMORY;
    }

    pDevice->pAdapter = pDevExt;

    //save d3d runtime device handler as paramenter when call dxgk
    pDevice->hDevice = pCreateDevice->hDevice;
    //return svga device handler to d3d runtime
    pCreateDevice->hDevice = pDevice;

    if (pCreateDevice->Flags.SystemDevice)
        pDevice->enmType = VBOXWDDM_DEVICE_TYPE_SYSTEM;

    pCreateDevice->pInfo = NULL;

    LOGI("pCreateContext(0x%p):\n", pCreateDevice);
    LOGI("  ->hDevice:0x%p\n", pCreateDevice->hDevice);
    LOGI("  ->Flags->SystemDevice:%ud\n", SystemDevice);
    LOGI("  ->Flags->GdiDevice:%ud\n", GdiDevice);
    LOGI("  ->Flags->DXGK_DEVICE_RESERVED0:%ud\n", vDXGK_DEVICE_RESERVED0);
    LOGI("  ->pInfo(0x%p):\n", pCreateDevice->pInfo);
    LOGI("  ->Pasid:%ud\n", pCreateDevice->Pasid);
    LOGI("  ->hKmdProcess:%ud\n", pCreateDevice->hKmdProcess);

    LOGI("<== context(0x%p)\n", hAdapter);

    PUNICODE_STRING currentProcessName;
    if (!GetCurrentProcessName(&currentProcessName)) {
        return STATUS_SUCCESS;
    }
    UNICODE_STRING dwmApp;
    RtlInitUnicodeString(&dwmApp, L"dwm.exe");
    if (!RtlCompareUnicodeString(currentProcessName, &dwmApp, TRUE)){
        pDevExt->dwmR0Process = PsGetCurrentProcess();
    }
    ExFreePoolWithTag(currentProcessName, SVGA_MEMTAG);

    return STATUS_SUCCESS;
}

NTSTATUS DxgkDdiDispatchIoRequest(
    IN CONST PVOID MiniportDeviceContext,
    IN ULONG VidPnSourceId,
    IN PVIDEO_REQUEST_PACKET VideoRequestPacket
)
{
    RT_NOREF(VidPnSourceId);

    LOGE("DxgkDdiDispatchIoRequest\n");

    return STATUS_SUCCESS;
}

NTSTATUS DxgkDdiQueryDeviceDescriptor(
    IN CONST PVOID MiniportDeviceContext,
    IN ULONG ChildUid,
    IN OUT PDXGK_DEVICE_DESCRIPTOR DeviceDescriptor
)
{
    /* The DxgkDdiQueryDeviceDescriptor should be made pageable. */
    PAGED_CODE();

    RT_NOREF(ChildUid, DeviceDescriptor);

    LOGI("==>, context(0x%p)\n", MiniportDeviceContext);

    /* we do not support EDID */
    return STATUS_MONITOR_NO_DESCRIPTOR;
}

NTSTATUS DxgkDdiQueryChildStatus(
    IN CONST PVOID  MiniportDeviceContext,
    IN PDXGK_CHILD_STATUS  ChildStatus,
    IN BOOLEAN  NonDestructiveOnly
    )
{
    /* The DxgkDdiQueryChildStatus should be made pageable. */
    PAGED_CODE();

    RT_NOREF(NonDestructiveOnly);

    LOGI("==> context(0x%p)\n", MiniportDeviceContext);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    if (ChildStatus->ChildUid >= (UINT32)VBoxCommonFromDeviceExt(pDevExt)->cDisplays)
    {
        LOGE("Invalid child id %d\n", ChildStatus->ChildUid);
        return STATUS_INVALID_PARAMETER;
    }

    NTSTATUS Status = STATUS_SUCCESS;
    switch (ChildStatus->Type)
    {
        case StatusConnection:
        {
            LOGI("StatusConnection\n");
            VBOXWDDM_TARGET *pTarget = &pDevExt->aTargets[ChildStatus->ChildUid];
            BOOLEAN Connected = !!pTarget->fConnected;
            if (!Connected)
                LOGI("Tgt[%d] DISCONNECTED!!\n", ChildStatus->ChildUid);
            ChildStatus->HotPlug.Connected = !!pTarget->fConnected;
            break;
        }
        case StatusRotation:
            LOGI("StatusRotation\n");
            ChildStatus->Rotation.Angle = 0;
            break;
        default:
            LOGE("ERROR: status type: %d\n", ChildStatus->Type);
            Status = STATUS_INVALID_PARAMETER;
            break;
    }

    LOGI("<== context(0x%p)\n", MiniportDeviceContext);

    return Status;
}

NTSTATUS DxgkDdiQueryChildRelations(
    IN CONST PVOID MiniportDeviceContext,
    IN OUT PDXGK_CHILD_DESCRIPTOR ChildRelations,
    IN ULONG ChildRelationsSize
    )
{
    /* The DxgkDdiQueryChildRelations function should be made pageable. */
    PAGED_CODE();

    RT_NOREF(ChildRelationsSize);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    LOGI("==> context(0x%p)\n", MiniportDeviceContext);
    LOGI("cDisplay=%d\n", VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
    for (int i = 0; i < VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
    {
        ChildRelations[i].ChildDeviceType = TypeVideoOutput;
        ChildRelations[i].ChildCapabilities.Type.VideoOutput.InterfaceTechnology = D3DKMDT_VOT_HD15; /* VGA */
        ChildRelations[i].ChildCapabilities.Type.VideoOutput.MonitorOrientationAwareness = D3DKMDT_MOA_NONE;
        ChildRelations[i].ChildCapabilities.Type.VideoOutput.SupportsSdtvModes = FALSE;
        ChildRelations[i].ChildCapabilities.HpdAwareness = HpdAwarenessInterruptible;
        ChildRelations[i].AcpiUid =  0;
        ChildRelations[i].ChildUid = i; /* should be == target id */
    }
    LOGI("<== context(0x%p)\n", MiniportDeviceContext);
    return STATUS_SUCCESS;
}

static VOID DxgkDdiDpcRoutine(const PVOID MiniportDeviceContext)
{
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    GaDxgkDdiDpcRoutine(MiniportDeviceContext);

    pDevExt->u.primary.DxgkInterface.DxgkCbNotifyDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);
}

static BOOLEAN DxgkDdiInterruptRoutine(const PVOID MiniportDeviceContext,
                                       ULONG MessageNumber)
{
    LOGI("==>, context(0x%p)\n", MiniportDeviceContext);
    BOOLEAN const fVMSVGA = GaDxgkDdiInterruptRoutine(MiniportDeviceContext, MessageNumber);
    LOGI("<==, context(0x%p)\n", MiniportDeviceContext);
    return fVMSVGA;
}

void FreeHwMonoCursorUnsupportedAppList(HW_MONO_CURSOR_UNSUPPORTED_APPS* pList)
{
    if (pList->AppNames)
    {
        ExFreePoolWithTag(pList->AppNames, SVGA_MEMTAG);
        pList->AppNames = NULL;
    }
    if (pList->RawBuf)
    {
        ExFreePoolWithTag(pList->RawBuf, SVGA_MEMTAG);
        pList->RawBuf = NULL;
    }
    pList->AppCount = 0;
}

NTSTATUS GetHwMonoCursorUnsupportedAppList(PDEVICE_OBJECT pDev, HW_MONO_CURSOR_UNSUPPORTED_APPS* pList)
{
    PAGED_CODE();

    NTSTATUS Status = STATUS_SUCCESS;
    UNICODE_STRING UnicodeStrValueName;
    ULONG ReqLen;
    PKEY_VALUE_PARTIAL_INFORMATION Buf = NULL;
    HANDLE DevInstRegKeyHandle;
    ULONG AppCount = 0;
    LPWCH* AppNames = NULL;

    RtlInitUnicodeString(&UnicodeStrValueName, L"HwMonoCursorUnsupportedApps");

    do
    {
        Status = IoOpenDeviceRegistryKey(pDev, PLUGPLAY_REGKEY_DRIVER, KEY_READ, &DevInstRegKeyHandle);
        if (!NT_SUCCESS(Status))
            break;

        Status = ZwQueryValueKey(DevInstRegKeyHandle, &UnicodeStrValueName, KeyValuePartialInformation, NULL, 0, &ReqLen);
        if (Status != STATUS_BUFFER_TOO_SMALL)
            break;

        Buf = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ReqLen, SVGA_MEMTAG);
        if (!Buf)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        Status = ZwQueryValueKey(DevInstRegKeyHandle, &UnicodeStrValueName, KeyValuePartialInformation, Buf, ReqLen, &ReqLen);
        if (!NT_SUCCESS(Status))
            break;

        if (Buf->Type != REG_MULTI_SZ)
        {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        for (LPWCH pos = (LPWCH)Buf->Data; *pos && (PBYTE)pos < Buf->Data + Buf->DataLength; pos += wcslen(pos) + 1, AppCount++);

        AppNames = (LPWCH*)ExAllocatePool2(POOL_FLAG_NON_PAGED, AppCount * sizeof(LPWCH), SVGA_MEMTAG);
        if (!AppNames)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        UINT i = 0;
        for (LPWCH pos = (LPWCH)Buf->Data; *pos && (PBYTE)pos < Buf->Data + Buf->DataLength; pos += wcslen(pos) + 1, i++)
        {
            AppNames[i] = pos;
        }

        pList->RawBuf = Buf;
        pList->AppCount = AppCount;
        pList->AppNames = AppNames;

        Status = STATUS_SUCCESS;
    } while (0);

    ZwClose(DevInstRegKeyHandle);

    if (!NT_SUCCESS(Status))
    {
        if (Buf)
            ExFreePoolWithTag(Buf, SVGA_MEMTAG);
        if (AppNames)
            ExFreePoolWithTag(AppNames, SVGA_MEMTAG);
    }

    return Status;
}

NTSTATUS DxgkDdiRemoveDevice(
    IN CONST PVOID MiniportDeviceContext
    )
{
    /* DxgkDdiRemoveDevice should be made pageable. */
    PAGED_CODE();

    LOGI("==>, context(0x%p)\n", MiniportDeviceContext);

    vboxWddmMemFree(MiniportDeviceContext);

    LOGI("<==\n");

    return STATUS_SUCCESS;
}

static NTSTATUS vboxWddmSetupDisplays(PVBOXMP_DEVEXT pDevExt)
{
    ULONG ulAvailable = VBoxCommonFromDeviceExt(pDevExt)->cbVRAM;

    /* vboxWddmVramCpuVisibleSize uses this value */
    pDevExt->cbVRAMCpuVisible = ulAvailable;

    NTSTATUS Status = pDevExt->u.primary.DxgkInterface.DxgkCbMapMemory(pDevExt->u.primary.DxgkInterface.DeviceHandle,
            VBoxCommonFromDeviceExt(pDevExt)->phVRAM,
            pDevExt->cbVRAMCpuVisible,
            FALSE, /* IN BOOLEAN InIoSpace */
            FALSE, /* IN BOOLEAN MapToUserMode */
            MmCached, //MmCached, /* IN MEMORY_CACHING_TYPE CacheType */
            (PVOID *)&pDevExt->pvVisibleVram /*OUT PVOID *VirtualAddress*/
            );

    if (!NT_SUCCESS(Status))
    {
        LOGE("VRAM mapped failed!\n");
        pDevExt->pvVisibleVram = NULL;
    }
    return Status;
}

NTSTATUS wddmMapRegisterResource(PVBOXMP_DEVEXT pDevExt)
{
    VBOXWDDM_HWRESOURCES& res = pDevExt->HwResources;
    mappedSysIoPort = NULL;
    NTSTATUS Status = 0;

    Status = pDevExt->u.primary.DxgkInterface.DxgkCbMapMemory(pDevExt->u.primary.DxgkInterface.DeviceHandle,
        res.phSysIO,
        res.cbSysIO,
        FALSE, /* IN BOOLEAN InIoSpace */
        FALSE, /* IN BOOLEAN MapToUserMode */
        MmCached, //MmNonCached, /* IN MEMORY_CACHING_TYPE CacheType */
        &pDevExt->sysIORegisterBase /*OUT PVOID *VirtualAddress*/
    );
    if (!NT_SUCCESS(Status))
    {
        LOGE("Map IO base failed!\n");
        return Status;
    }
    LOGI("wddmMapRegisterResource %016llx, %016p\n", res.phSysIO.QuadPart, pDevExt->sysIORegisterBase);

    if (pDevExt->sysIORegisterBase) {
        mappedSysIoPort = pDevExt->sysIORegisterBase;
    }

    return Status;
}

NTSTATUS vboxWddmPickResources(PDXGK_DEVICE_INFO pDeviceInfo, PVBOXWDDM_HWRESOURCES pHwResources)
{
    NTSTATUS Status = STATUS_SUCCESS;
    USHORT DispiId;
    memset(pHwResources, 0, sizeof (*pHwResources));
    pHwResources->cbVRAM = VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES;

    DispiId = VBE_DISPI_ID2;

    LOGI("found the VBE card\n");
    /*
    * Write some hardware information to registry, so that
    * it's visible in Windows property dialog.
    */

    /*
    * Query the adapter's memory size. It's a bit of a hack, we just read
    * an ULONG from the data port without setting an index before.
    */
    PCM_RESOURCE_LIST pRcList = pDeviceInfo->TranslatedResourceList;
    for (ULONG i = 0; i < pRcList->Count; ++i)
    {
        PCM_FULL_RESOURCE_DESCRIPTOR pFRc = &pRcList->List[i];
        for (ULONG j = 0; j < pFRc->PartialResourceList.Count; ++j)
        {
            PCM_PARTIAL_RESOURCE_DESCRIPTOR pPRc = &pFRc->PartialResourceList.PartialDescriptors[j];
            LOGI("membar %d %d %d\n", i, j, pPRc->Type);
            switch (pPRc->Type)
            {
                case CmResourceTypePort:
                    pHwResources->phIO = pPRc->u.Port.Start;
                    pHwResources->cbIO = pPRc->u.Port.Length;
                    LOGI("IO 0x%016llx[%d]\n", pHwResources->phIO.QuadPart, pHwResources->cbIO);
                    break;
                case CmResourceTypeInterrupt:
                    break;
                case CmResourceTypeMemory:
                    if (pPRc->u.Memory.Length == 0x10) {
                        pHwResources->phIO = pPRc->u.Memory.Start;
                        pHwResources->cbIO = pPRc->u.Memory.Length;
                        LOGI("mem io 0x%016llx[%d]\n", pHwResources->phIO.QuadPart, pHwResources->cbIO);
                        break;
                    }
                    if (pPRc->u.Memory.Length == 0x20000) {
                        pHwResources->phSysIO = pPRc->u.Memory.Start;
                        pHwResources->cbSysIO = pPRc->u.Memory.Length;
                        LOGI("mem sysio 0x%016llx[%d]\n", pHwResources->phSysIO.QuadPart, pHwResources->cbSysIO);
                        break;
                    }
                    if (pHwResources->phVRAM.QuadPart) {
                        pHwResources->phFIFO = pPRc->u.Memory.Start;
                        pHwResources->cbFIFO = pPRc->u.Memory.Length;
                        LOGI("fifo 0x%016llx[%d]\n", pHwResources->phFIFO.QuadPart, pHwResources->cbFIFO);
                        break;
                    }
                    pHwResources->phVRAM = pPRc->u.Memory.Start;
                    pHwResources->ulApertureSize = pPRc->u.Memory.Length;
                    pHwResources->cbVRAM = pHwResources->ulApertureSize;
                    LOGI("vram 0x%016llx[%d]\n", pHwResources->phVRAM.QuadPart, pHwResources->cbVRAM);
                    break;
                case CmResourceTypeDma:
                    break;
                case CmResourceTypeDeviceSpecific:
                    break;
                case CmResourceTypeBusNumber:
                    break;
                default:
                    break;
            }
        }
    }

    return Status;
}

NTSTATUS DxgkDdiStartDevice(
    IN CONST PVOID  MiniportDeviceContext,
    IN PDXGK_START_INFO  DxgkStartInfo,
    IN PDXGKRNL_INTERFACE  DxgkInterface,
    OUT PULONG  NumberOfVideoPresentSources,
    OUT PULONG  NumberOfChildren
    )
{
    /* The DxgkDdiStartDevice function should be made pageable. */
    PAGED_CODE();

    NTSTATUS Status;

    LOGE("Begin start device\n");

    if (!(ARGUMENT_PRESENT(MiniportDeviceContext) &&
        ARGUMENT_PRESENT(DxgkInterface) &&
        ARGUMENT_PRESENT(DxgkStartInfo) &&
        ARGUMENT_PRESENT(NumberOfVideoPresentSources) &&
        ARGUMENT_PRESENT(NumberOfChildren)
        ))
    {
        LOGF("invalid parameter, returning err\n");
        return STATUS_INVALID_PARAMETER;
    }
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    vboxWddmVGuidGet(pDevExt);

    /* Save DeviceHandle and function pointers supplied by the DXGKRNL_INTERFACE structure passed to DxgkInterface. */
    memcpy(&pDevExt->u.primary.DxgkInterface, DxgkInterface, sizeof (DXGKRNL_INTERFACE));

    /* Allocate a DXGK_DEVICE_INFO structure, and call DxgkCbGetDeviceInformation to fill in the members of that structure, which include the registry path, the PDO, and a list of translated resources for the display adapter represented by MiniportDeviceContext. Save selected members (ones that the display miniport driver will need later)
        * of the DXGK_DEVICE_INFO structure in the context block represented by MiniportDeviceContext. */
    DXGK_DEVICE_INFO DeviceInfo;
    memset(&DeviceInfo, 0, sizeof(DXGK_DEVICE_INFO));
    Status = pDevExt->u.primary.DxgkInterface.DxgkCbGetDeviceInformation (pDevExt->u.primary.DxgkInterface.DeviceHandle, &DeviceInfo);
    if (Status != STATUS_SUCCESS)
    {
        LOGE("DxgkCbGetDeviceInformation failed Status(0x%x), returning err\n", Status);
        return Status;
    }
    Status = vboxWddmPickResources(&DeviceInfo, &pDevExt->HwResources);
    wddmMapRegisterResource(pDevExt);

    InitQemuLog();
    LOGI("Init qemu Log enabled ----------------------------------\n");

    if (Status != STATUS_SUCCESS)
    {
        LOGE("vboxWddmPickResources failed Status(0x%x), returning err\n", Status);
        return STATUS_UNSUCCESSFUL;
    }
    /* Figure out the host capabilities. Start with nothing. */
    pDevExt->fComplexTopologiesEnabled = TRUE;

    if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VBOX)
    {
        pDevExt->f3DEnabled = FALSE;
    }
    else if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA)
    {
        if (pDevExt->f3DEnabled)
        {
            pDevExt->fComplexTopologiesEnabled = TRUE;
        }
    }
    else
    {
        pDevExt->f3DEnabled = FALSE;
    }

    LOGI("Handling complex topologies %s \n", pDevExt->fComplexTopologiesEnabled ? "enabled" : "disabled");

    /* Guest supports only HGSMI, the old VBVA via VMMDev is not supported.
        * The host will however support both old and new interface to keep compatibility
        * with old guest additions.
        */
    LOGI("mappedSysIoPort %016p \n", mappedSysIoPort);
    PVBOXMP_COMMON pCommon = VBoxCommonFromDeviceExt(pDevExt);
    memset(VBoxCommonFromDeviceExt(pDevExt), 0, sizeof(*VBoxCommonFromDeviceExt(pDevExt)));
    pCommon->phVRAM = pDevExt->HwResources.phVRAM;
    pCommon->ulApertureSize = pDevExt->HwResources.ulApertureSize;
    pCommon->cbVRAM = pDevExt->HwResources.cbVRAM;
    pCommon->cDisplays = 1;

    Status = vboxWddmSetupDisplays(pDevExt);
    if (Status != STATUS_SUCCESS)
    {
        LOGE("vboxWddmSetupDisplays failed Status(0x%x), returning err\n", Status);
        return STATUS_UNSUCCESSFUL;
    }

    *NumberOfVideoPresentSources = VBoxCommonFromDeviceExt(pDevExt)->cDisplays;
    *NumberOfChildren = VBoxCommonFromDeviceExt(pDevExt)->cDisplays;
    LOGI("sources(%d), children(%d)\n", *NumberOfVideoPresentSources, *NumberOfChildren);

    VBoxCommonFromDeviceExt(pDevExt)->fAnyX = VBoxVideoAnyWidthAllowed();

    Status = VBoxWddmSlInit(pDevExt);
    if (Status != STATUS_SUCCESS)
    {
        LOGE("VBoxWddmSlInit failed Status(0x%x), returning err\n", Status);
        return Status;
    }

    for (UINT i = 0; i < (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
    {
        PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[i];
        KeInitializeSpinLock(&pSource->AllocationLock);
    }

    DWORD dwVal = VBOXWDDM_CFG_DRV_DEFAULT;
    HANDLE hKey = NULL;
    WCHAR aNameBuf[100];

    Status = IoOpenDeviceRegistryKey(pDevExt->pPDO, PLUGPLAY_REGKEY_DRIVER, GENERIC_READ, &hKey);
    if (!NT_SUCCESS(Status))
    {
        LOGF("IoOpenDeviceRegistryKey failed, Status = 0x%x\n", Status);
        hKey = NULL;
    }

    if (hKey)
    {
        Status = vboxWddmRegQueryValueDword(hKey, VBOXWDDM_REG_DRV_FLAGS_NAME, &dwVal);
        if (!NT_SUCCESS(Status))
        {
            LOGI("vboxWddmRegQueryValueDword failed, Status = 0x%x\n", Status);
            dwVal = VBOXWDDM_CFG_DRV_DEFAULT;
        }
    }

    pDevExt->dwDrvCfgFlags = dwVal;

    for (UINT i = 0; i < (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
    {
        PVBOXWDDM_TARGET pTarget = &pDevExt->aTargets[i];
        if (i == 0 || (pDevExt->dwDrvCfgFlags & VBOXWDDM_CFG_DRV_SECONDARY_TARGETS_CONNECTED) || !hKey)
        {
            pTarget->fConnected = true;
            pTarget->fConfigured = true;
        }
        else if (hKey)
        {
            swprintf(aNameBuf, L"%s%d\n", VBOXWDDM_REG_DRV_DISPFLAGS_PREFIX, i);
            Status = vboxWddmRegQueryValueDword(hKey, aNameBuf, &dwVal);
            if (NT_SUCCESS(Status))
            {
                pTarget->fConnected = !!(dwVal & VBOXWDDM_CFG_DRVTARGET_CONNECTED);
                pTarget->fConfigured = true;
            }
            else
            {
                LOGF("vboxWddmRegQueryValueDword failed, Status = 0x%x\n", Status);
                pTarget->fConnected = false;
                pTarget->fConfigured = false;
            }
        }
    }

    if (hKey)
    {
        ZwClose(hKey);
    }

    if (g_windowsVersion >= WINVERSION_8)
    {
        DXGK_DISPLAY_INFORMATION DisplayInfo;
        Status = pDevExt->u.primary.DxgkInterface.DxgkCbAcquirePostDisplayOwnership(
            pDevExt->u.primary.DxgkInterface.DeviceHandle, &DisplayInfo);
        if (NT_SUCCESS(Status))
        {
            PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[0];
            PHYSICAL_ADDRESS PhAddr;
            /* display info may sometimes not be valid, e.g. on from-full-graphics wddm driver update
                * ensure we have something meaningful here */
            if (!DisplayInfo.Width)
            {
                PhAddr = VBoxCommonFromDeviceExt(pDevExt)->phVRAM;
                vboxWddmDiInitDefault(&DisplayInfo, PhAddr, 0);
            }
            else
            {
                PhAddr = DisplayInfo.PhysicAddress;
                DisplayInfo.TargetId = 0;
            }

            vboxWddmDiToAllocData(pDevExt, &DisplayInfo, &pSource->AllocData);

            /* init the rest source infos with some default values */
            for (UINT i = 1; i < (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
            {
                PhAddr.QuadPart += pSource->AllocData.SurfDesc.cbSize;
                PhAddr.QuadPart = ROUND_TO_PAGES(PhAddr.QuadPart);
                vboxWddmDiInitDefault(&DisplayInfo, PhAddr, i);
                pSource = &pDevExt->aSources[i];
                vboxWddmDiToAllocData(pDevExt, &DisplayInfo, &pSource->AllocData);
            }
        }
        else
        {
            LOGE("DxgkCbAcquirePostDisplayOwnership failed, Status 0x%x\n", Status);
        }
    }

    GetHwMonoCursorUnsupportedAppList(pDevExt->pPDO, &pDevExt->HwMonoCursorUnsupportedApps);

    if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA)
    {
        LOGI("WDDM: VRAM 0x%llx/%lu, FIFO 0x%llx/%lu, IO 0x%llx/%lu\n",
                pDevExt->HwResources.phVRAM.QuadPart, pDevExt->HwResources.cbVRAM,
                pDevExt->HwResources.phFIFO.QuadPart, pDevExt->HwResources.cbFIFO,
                pDevExt->HwResources.phIO.QuadPart, pDevExt->HwResources.cbIO);

        Status = GaAdapterStart(pDevExt);
        if (Status == STATUS_SUCCESS)
        { /* likely */ }
        else
            LOGE("WDDM: GaAdapterStart failed Status(0x%x)\n", Status);
    }

    LOGE("End start device: status(0x%x)\n", Status);

    return Status;
}

static void vboxWddmDevExtZeroinit(PVBOXMP_DEVEXT pDevExt, CONST PDEVICE_OBJECT pPDO)
{
    memset(pDevExt, 0, sizeof (VBOXMP_DEVEXT));
    pDevExt->pPDO = pPDO;
    PWCHAR pName = (PWCHAR)(((UINT8*)pDevExt) + VBOXWDDM_ROUNDBOUND(sizeof(VBOXMP_DEVEXT), 8));
    RtlInitUnicodeString(&pDevExt->RegKeyName, pName);

    VBoxVidPnSourcesInit(pDevExt->aSources, RT_ELEMENTS(pDevExt->aSources), 0);

    VBoxVidPnTargetsInit(pDevExt->aTargets, RT_ELEMENTS(pDevExt->aTargets), 0);

    pDevExt->enmHwType = VBOXVIDEO_HWTYPE_VMSVGA;
    if (g_VBoxDisplayOnly)
    {
        pDevExt->f3DEnabled = FALSE;
    }
    else
    {
        pDevExt->f3DEnabled = TRUE;
    }
}

static void vboxWddmFreeDisplays(PVBOXMP_DEVEXT pDevExt)
{
    if (pDevExt->pvVisibleVram)
    {
        pDevExt->u.primary.DxgkInterface.DxgkCbUnmapMemory(pDevExt->u.primary.DxgkInterface.DeviceHandle,
            pDevExt->pvVisibleVram);
        pDevExt->pvVisibleVram = NULL;
    }
}

static void wddmUnmapRegisterResource(PVBOXMP_DEVEXT pDevExt)
{
    if (pDevExt->sysIORegisterBase)
    {
        pDevExt->u.primary.DxgkInterface.DxgkCbUnmapMemory(pDevExt->u.primary.DxgkInterface.DeviceHandle,
            pDevExt->sysIORegisterBase);
        pDevExt->sysIORegisterBase = NULL;
    }
    mappedSysIoPort = NULL;
}

NTSTATUS DxgkDdiStopDevice(
    IN CONST PVOID MiniportDeviceContext)
{
    /* The DxgkDdiStopDevice function should be made pageable. */
    PAGED_CODE();

    LOGI("==> context(0x%p)\n", MiniportDeviceContext);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;
    NTSTATUS Status = STATUS_SUCCESS;

    if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA)
    {
        GaAdapterStop(pDevExt);
    }

    EXT_DELETE_PARAMETERS params;
    ExInitializeDeleteTimerParameters(&params);
    ExDeleteTimer(pDevExt->VSyncTimer, TRUE, TRUE, &params);
    pDevExt->VSyncTimer = NULL;

    vboxWddmFreeDisplays(pDevExt);

    vboxWddmVGuidFree(pDevExt);

    VBoxWddmVModesCleanup();

    FreeHwMonoCursorUnsupportedAppList(&pDevExt->HwMonoCursorUnsupportedApps);

    /* revert back to the state we were right after the DxgkDdiAddDevice */
    vboxWddmDevExtZeroinit(pDevExt, pDevExt->pPDO);
    LOGI("<== status:%d\n", Status);
    
    wddmUnmapRegisterResource(pDevExt);
    return Status;
}

NTSTATUS DxgkDdiAddDevice(
    IN CONST PDEVICE_OBJECT PhysicalDeviceObject,
    OUT PVOID *MiniportDeviceContext
    )
{
    /* The DxgkDdiAddDevice function should be made pageable. */
    PAGED_CODE();

    LOGI("==> pdo(0x%p)\n",  PhysicalDeviceObject);

    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXMP_DEVEXT pDevExt = NULL;
    WCHAR RegKeyBuf[512];
    ULONG cbRegKeyBuf = sizeof (RegKeyBuf);

    Status = IoGetDeviceProperty(PhysicalDeviceObject,
                                 DevicePropertyDriverKeyName,
                                 cbRegKeyBuf,
                                 RegKeyBuf,
                                 &cbRegKeyBuf);

    if (Status == STATUS_SUCCESS)
    {
        pDevExt = (PVBOXMP_DEVEXT)vboxWddmMemAllocZero(VBOXWDDM_ROUNDBOUND(sizeof(VBOXMP_DEVEXT), 8) + cbRegKeyBuf);
        if (pDevExt)
        {
            PWCHAR pName = (PWCHAR)(((UINT8*)pDevExt) + VBOXWDDM_ROUNDBOUND(sizeof(VBOXMP_DEVEXT), 8));
            memcpy(pName, RegKeyBuf, cbRegKeyBuf);
            vboxWddmDevExtZeroinit(pDevExt, PhysicalDeviceObject);
            pDevExt->AdapterPowerState = PowerDeviceD0;
            *MiniportDeviceContext = pDevExt;
        }
        else
        {
            Status  = STATUS_NO_MEMORY;
            LOGE("ERROR, failed to create context\n");
        }
    }

    LOGI("<== Status(0x%x), pDevExt(0x%p)\n", Status, pDevExt);

    return Status;
}

NTSTATUS DxgkDdiSetPowerState(
    IN CONST PVOID MiniportDeviceContext,
    IN ULONG DeviceUid,
    IN DEVICE_POWER_STATE DevicePowerState,
    IN POWER_ACTION ActionType
)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    /* The DxgkDdiSetPowerState function should be made pageable. */
    PAGED_CODE();

    LOGI("MiniportDeviceContext(0x%p)\n", MiniportDeviceContext);

    LOGE("ActionType=%u, DevicePowerState=%u\n", ActionType, DevicePowerState);

    // No action on reboot and normal shutdown, just work for fast startup
    if (ActionType == PowerActionShutdownReset || ActionType == PowerActionShutdownOff || ActionType == PowerActionShutdown) {
        return STATUS_SUCCESS;
    }

    if (DeviceUid == DISPLAY_ADAPTER_HW_ID) {
        if (DevicePowerState == PowerDeviceD0) {
            DXGK_DISPLAY_INFORMATION DisplayInfo;
            Status = pDevExt->u.primary.DxgkInterface.DxgkCbAcquirePostDisplayOwnership(
            pDevExt->u.primary.DxgkInterface.DeviceHandle, &DisplayInfo);
            if (!NT_SUCCESS(Status)) {
                LOGE("DxgkCbAcquirePostDisplayOwnership failed with status 0x%X Width = %d\n", Status, DisplayInfo.Width);
            }

            if (pDevExt->AdapterPowerState == PowerDeviceD3) {
                DXGKARG_SETVIDPNSOURCEVISIBILITY Visibility;
                Visibility.VidPnSourceId = 0; //D3DDDI_ID_ALL;
                Visibility.Visible = FALSE;
                DXGKARG_SETVIDPNSOURCEVISIBILITY *pSetVidPnSourceVisibility = &Visibility;

                PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[pSetVidPnSourceVisibility->VidPnSourceId];
                PVBOXWDDM_ALLOCATION pAllocation = pSource->pPrimaryAllocation;
                if (pAllocation) {
                    pAllocation->bVisible = pSetVidPnSourceVisibility->Visible;
                }

                if (pSource->bVisible != pSetVidPnSourceVisibility->Visible) {
                    pSource->bVisible = pSetVidPnSourceVisibility->Visible;
                }

                if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA) {
                    GaVidPnSourceCheckPos(pDevExt, pSetVidPnSourceVisibility->VidPnSourceId);
                }
            }

            pDevExt->AdapterPowerState = DevicePowerState;
        }

        switch (DevicePowerState) {
            case PowerDeviceUnspecified:
            case PowerDeviceD0: {
                DXGK_DEVICE_INFO DeviceInfo;
                memset(&DeviceInfo, 0, sizeof(DXGK_DEVICE_INFO));
                Status = pDevExt->u.primary.DxgkInterface.DxgkCbGetDeviceInformation(pDevExt->u.primary.DxgkInterface.DeviceHandle, &DeviceInfo);
                if (Status != STATUS_SUCCESS) {
                    LOGE("DxgkCbGetDeviceInformation failed Status(0x%x)\n", Status);
                }
                Status = vboxWddmPickResources(&DeviceInfo, &pDevExt->HwResources);
                if (Status != STATUS_SUCCESS) {
                    LOGE("wddm pick resource failed Status(0x%x)\n", Status);
                }

                wddmMapRegisterResource(pDevExt);
                Status = vboxWddmSetupDisplays(pDevExt);
                if (Status != STATUS_SUCCESS) {
                    LOGE("vboxWddmSetupDisplays failed Status(0x%x)\n", Status);
                }
                LOGE("ActionType=%u, DevicePowerState=%u\n", ActionType, DevicePowerState);

                Status = VBoxWddmSlInit(pDevExt);
                if (Status != STATUS_SUCCESS) {
                    LOGE("VBoxWddmSlInit failed Status(0x%x)\n", Status);
                }

                GaAdapterStart(pDevExt);
                LOGE("GaAdapterStart in PowerDeviceD0\n");
            } break;
            case PowerDeviceD1:
            case PowerDeviceD2:
            case PowerDeviceD3: {
                LOGE("GaAdapterStop in PowerDeviceD3\n");
                GaAdapterStop(pDevExt);
                EXT_DELETE_PARAMETERS params;
                ExInitializeDeleteTimerParameters(&params);
                ExDeleteTimer(pDevExt->VSyncTimer, TRUE, TRUE, &params);
                pDevExt->VSyncTimer = NULL;
                vboxWddmFreeDisplays(pDevExt);
                wddmUnmapRegisterResource(pDevExt);

                if (pDevExt->dwmR0Process != NULL) {
                    HANDLE hProcessHandle = NULL;
                    Status = ObOpenObjectByPointer(pDevExt->dwmR0Process, OBJ_KERNEL_HANDLE, NULL, 0, 0, KernelMode, &hProcessHandle);
                    if (NT_SUCCESS(Status)) {
                        ZwTerminateProcess(hProcessHandle, STATUS_SUCCESS);
                        ZwClose(hProcessHandle);
                        pDevExt->dwmR0Process = NULL;
                    }
                }
            } break;
        }
    }

    return STATUS_SUCCESS;
}

NTSTATUS DxgkDdiNotifyAcpiEvent(
    IN CONST PVOID  MiniportDeviceContext,
    IN DXGK_EVENT_TYPE  EventType,
    IN ULONG  Event,
    IN PVOID  Argument,
    OUT PULONG  AcpiFlags
)
{
    RT_NOREF(MiniportDeviceContext, EventType, Event, Argument, AcpiFlags);

    LOGE("DxgkDdiNotifyAcpiEvent\n");

    return STATUS_SUCCESS;
}

VOID DxgkDdiResetDevice(
    IN CONST PVOID MiniportDeviceContext
)
{
    RT_NOREF(MiniportDeviceContext);

    LOGE("DxgkDdiResetDevice\n");
}

VOID DxgkDdiUnload(
    VOID
)
{
    /* DxgkDdiUnload should be made pageable. */
    PAGED_CODE();

    LOGF("unloading\n");
}

NTSTATUS DxgkDdiQueryInterface(
    IN CONST PVOID MiniportDeviceContext,
    IN PQUERY_INTERFACE QueryInterface
)
{
    RT_NOREF(MiniportDeviceContext, QueryInterface);

    return STATUS_NOT_SUPPORTED;
}

VOID DxgkDdiControlEtwLogging(
    IN BOOLEAN  Enable,
    IN ULONG  Flags,
    IN UCHAR  Level
)
{
    RT_NOREF(Enable, Flags, Level);

    LOGF("\n");
}

#ifdef VBOX_WITH_VMSVGA3D_DX
typedef struct VBOXDXSEGMENTDESCRIPTOR
{
    DXGK_SEGMENTFLAGS Flags;
    PHYSICAL_ADDRESS  CpuTranslatedAddress;
    SIZE_T            Size;
} VBOXDXSEGMENTDESCRIPTOR;

#define VBOXDX_SEGMENTS_COUNT 3

static void vmsvgaDXGetSegmentDescription(PVBOXMP_DEVEXT pDevExt, int idxSegment, VBOXDXSEGMENTDESCRIPTOR *pDesc)
{
    /** @todo 2 segments for pDevExt->fLegacy flag. */
    /* 3 segments:
     * 1: The usual VRAM, CpuVisible;
     * 2: Aperture segment for guest backed objects;
     * 3: Host resources, CPU invisible.
     */
    RT_ZERO(*pDesc);
    if (idxSegment == 0)
    {
        pDesc->CpuTranslatedAddress = VBoxCommonFromDeviceExt(pDevExt)->phVRAM;
        pDesc->Size                 = pDevExt->cbVRAMCpuVisible & X86_PAGE_4K_BASE_MASK;
        pDesc->Flags.CpuVisible     = 1;
    }
    else if (idxSegment == 1)
    {
        pDesc->Size                 = _2G; /** @todo */
        pDesc->Flags.CpuVisible     = 1;
        pDesc->Flags.Aperture       = 1;
    }
    else if (idxSegment == 2)
    {
        pDesc->Size                 = _2G; /** @todo */
    }
}
#endif

/**
 * DxgkDdiQueryAdapterInfo
 */
NTSTATUS APIENTRY DxgkDdiQueryAdapterInfo(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo)
{
    /* The DxgkDdiQueryAdapterInfo should be made pageable. */
    PAGED_CODE();

    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    LOGW("context(0x%p), Query type (%d)\n", hAdapter, pQueryAdapterInfo->Type);

    switch (pQueryAdapterInfo->Type)
    {
    case DXGKQAITYPE_DRIVERCAPS:
    {
        DXGK_DRIVERCAPS *pCaps = (DXGK_DRIVERCAPS*)pQueryAdapterInfo->pOutputData;
        memset(pCaps, 0, pQueryAdapterInfo->OutputDataSize);

        /* driver talks to host in terms of page numbers when reffering to RAM
         * we use UINT32 field to pass page index to host, so max would be (~0UL) << PAGE_OFFSET,
         * which seems quite enough */
        pCaps->HighestAcceptableAddress.LowPart = ~0UL;
        pCaps->HighestAcceptableAddress.HighPart = PAGE_OFFSET_MASK;
        pCaps->MaxPointerWidth = VBOXWDDM_C_POINTER_MAX_WIDTH;
        pCaps->MaxPointerHeight = VBOXWDDM_C_POINTER_MAX_HEIGHT;
        // Enable MaskedColor, for I cursor
        pCaps->PointerCaps.Value = 3 | 4; /* Monochrome , Color*/ /* MaskedColor == Value | 4 */
        if (!g_VBoxDisplayOnly)
        {
            pCaps->MaxAllocationListSlotId = 16;
            pCaps->ApertureSegmentCommitLimit = 0;
            pCaps->InterruptMessageNumber = 0;
            pCaps->NumberOfSwizzlingRanges = 0;
            pCaps->MaxOverlays = 0;
            pCaps->GammaRampCaps.Value = 0;
            pCaps->PresentationCaps.Value = 0;
            pCaps->PresentationCaps.NoScreenToScreenBlt = 1;
            pCaps->PresentationCaps.NoOverlapScreenBlt = 1;
            pCaps->PresentationCaps.AlignmentShift = 2;
            pCaps->PresentationCaps.MaxTextureWidthShift = 2; /* Up to 8196 */
            pCaps->PresentationCaps.MaxTextureHeightShift = 2; /* Up to 8196 */
            pCaps->MaxQueuedFlipOnVSync = 0; /* do we need it? */
            pCaps->FlipCaps.Value = 0;
            /* ? pCaps->FlipCaps.FlipOnVSyncWithNoWait = 1; */
            pCaps->SchedulingCaps.Value = 0;
            /* we might need it for Aero.
                * Setting this flag means we support DeviceContext, i.e.
                *  DxgkDdiCreateContext and DxgkDdiDestroyContext
                */
            pCaps->SchedulingCaps.MultiEngineAware = 1;
            pCaps->MemoryManagementCaps.Value = 0;
            /** @todo this correlates with pCaps->SchedulingCaps.MultiEngineAware */
            pCaps->MemoryManagementCaps.PagingNode = 0;

            /* The Gallium node has NodeOrdinal == 0, because:
                *   GDI context is created with it;
                *   we generate commands for the context;
                *   there seems to be no easy way to distinguish which node a fence was completed for.
                *
                * GDI context is used for example for copying between D3DKMDT_STANDARDALLOCATION_SHADOWSURFACE
                * and D3DKMDT_STANDARDALLOCATION_SHAREDPRIMARYSURFACE.
                */
            pCaps->GpuEngineTopology.NbAsymetricProcessingNodes = 1;
            if (g_windowsVersion >= WINVERSION_8) {
                pCaps->WDDMVersion = DXGKDDI_WDDMv1_2;
                pCaps->SupportNonVGA = TRUE;
            }
        }
        else
        {
            pCaps->WDDMVersion = DXGKDDI_WDDMv1_2;
        }
        break;
    }
    case DXGKQAITYPE_QUERYSEGMENT:
    {
        if (!g_VBoxDisplayOnly)
        {
#ifdef VBOX_WITH_VMSVGA3D_DX
            if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA && SvgaIsDXSupported(pDevExt))
            {
                DXGK_QUERYSEGMENTOUT *pOut = (DXGK_QUERYSEGMENTOUT *)pQueryAdapterInfo->pOutputData;
                if (!pOut->pSegmentDescriptor)
                    pOut->NbSegment = VBOXDX_SEGMENTS_COUNT; /* Return the number of segments. */
                else if (pOut->NbSegment == VBOXDX_SEGMENTS_COUNT)
                {
                    DXGK_SEGMENTDESCRIPTOR *paDesc = pOut->pSegmentDescriptor;
                    for (unsigned i = 0; i < VBOXDX_SEGMENTS_COUNT; ++i)
                    {
                        VBOXDXSEGMENTDESCRIPTOR desc;
                        vmsvgaDXGetSegmentDescription(pDevExt, i, &desc);
                        paDesc[i].CpuTranslatedAddress = desc.CpuTranslatedAddress;
                        paDesc[i].Size                 = desc.Size;
                        paDesc[i].CommitLimit          = desc.Size;
                        paDesc[i].Flags                = desc.Flags;
                    }

                    pOut->PagingBufferSegmentId       = 0;
                    pOut->PagingBufferSize            = PAGE_SIZE;
                    pOut->PagingBufferPrivateDataSize = PAGE_SIZE;
                }
                else
                {
                    LOGE("NbSegment %d", pOut->NbSegment);
                    Status = STATUS_INVALID_PARAMETER;
                }
                break;
            }
#endif
            /* no need for DXGK_QUERYSEGMENTIN as it contains AGP aperture info, which (AGP aperture) we do not support
             * DXGK_QUERYSEGMENTIN *pQsIn = (DXGK_QUERYSEGMENTIN*)pQueryAdapterInfo->pInputData; */
            DXGK_QUERYSEGMENTOUT *pQsOut = (DXGK_QUERYSEGMENTOUT*)pQueryAdapterInfo->pOutputData;
# define VBOXWDDM_SEGMENTS_COUNT 2
            if (!pQsOut->pSegmentDescriptor)
            {
                /* we are requested to provide the number of segments we support */
                pQsOut->NbSegment = VBOXWDDM_SEGMENTS_COUNT;
            }
            else if (pQsOut->NbSegment != VBOXWDDM_SEGMENTS_COUNT)
            {
                LOGE("NbSegment (%d) != 1\n", pQsOut->NbSegment);
                Status = STATUS_INVALID_PARAMETER;
            }
            else
            {
                DXGK_SEGMENTDESCRIPTOR *pDr = pQsOut->pSegmentDescriptor;
                /* we are requested to provide segment information */
                pDr->BaseAddress.QuadPart = 0;
                pDr->CpuTranslatedAddress = VBoxCommonFromDeviceExt(pDevExt)->phVRAM;
                /* make sure the size is page aligned */
                /** @todo need to setup VBVA buffers and adjust the mem size here */
                pDr->Size = vboxWddmVramCpuVisibleSegmentSize(pDevExt);
                pDr->NbOfBanks = 0;
                pDr->pBankRangeTable = 0;
                pDr->CommitLimit = pDr->Size;
                pDr->Flags.Value = 0;
                pDr->Flags.CpuVisible = 1;

                ++pDr;
                /* create cpu-invisible segment of the same size */
                pDr->BaseAddress.QuadPart = 0;
                pDr->CpuTranslatedAddress.QuadPart = 0;
                /* make sure the size is page aligned */
                /** @todo need to setup VBVA buffers and adjust the mem size here */
                pDr->Size = vboxWddmVramCpuInvisibleSegmentSize(pDevExt);
                pDr->NbOfBanks = 0;
                pDr->pBankRangeTable = 0;
                pDr->CommitLimit = pDr->Size;
                pDr->Flags.Value = 0;

                pQsOut->PagingBufferSegmentId = 0;
                pQsOut->PagingBufferSize = 4096; // PAGE_SIZE
                pQsOut->PagingBufferPrivateDataSize = 4096; // PAGE_SIZE
            }
        }
        else
        {
            LOGF("unsupported Type (%d)\n", pQueryAdapterInfo->Type);
            Status = STATUS_NOT_SUPPORTED;
        }
        break;
    }
    case DXGKQAITYPE_UMDRIVERPRIVATE:
    {
        if (!g_VBoxDisplayOnly)
        {
            if (pQueryAdapterInfo->OutputDataSize >= sizeof(VBOXWDDM_QAI))
            {
                VBOXWDDM_QAI *pQAI = (VBOXWDDM_QAI *)pQueryAdapterInfo->pOutputData;
                memset(pQAI, 0, sizeof(VBOXWDDM_QAI));

                pQAI->u32Version = VBOXVIDEOIF_VERSION;
                pQAI->enmHwType = pDevExt->enmHwType;
                pQAI->u32AdapterCaps = pDevExt->f3DEnabled ? VBOXWDDM_QAI_CAP_3D : 0;
                pQAI->u32AdapterCaps |= VBOXWDDM_QAI_CAP_DXVA; /** @todo Fetch from registry. */
                if (VBoxQueryWinVersion(NULL) >= WINVERSION_7) {
                    pQAI->u32AdapterCaps |= VBOXWDDM_QAI_CAP_WIN7;
                }

                static int cLoggedCaps = 0;
                if (cLoggedCaps < 1)
                {
                    ++cLoggedCaps;
                    LOGI("WDDM: adapter capabilities 0x%08X\n", pQAI->u32AdapterCaps);
                }

                if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VBOX)
                {
                    pQAI->u.vbox.u32VBox3DCaps = 0;
                }
                else if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA)
                    GaQueryInfo(pDevExt->pGa, pDevExt->enmHwType, &pQAI->u.vmsvga.HWInfo);

            }
            else
            {
                LOGE("incorrect buffer size %d, expected %d\n", pQueryAdapterInfo->OutputDataSize, sizeof(VBOXWDDM_QAI));
                Status = STATUS_BUFFER_TOO_SMALL;
            }
        }
        else
        {
            LOGF("unsupported Type (%d)\n", pQueryAdapterInfo->Type);
            Status = STATUS_NOT_SUPPORTED;
        }
        break;
    }
    case DXGKQAITYPE_QUERYSEGMENT3:
    {
#ifdef VBOX_WITH_VMSVGA3D_DX
        if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA && SvgaIsDXSupported(pDevExt))
        {
            DXGK_QUERYSEGMENTOUT3 *pOut = (DXGK_QUERYSEGMENTOUT3 *)pQueryAdapterInfo->pOutputData;
            if (!pOut->pSegmentDescriptor)
                pOut->NbSegment = VBOXDX_SEGMENTS_COUNT; /* Return the number of segments. */
            else if (pOut->NbSegment == VBOXDX_SEGMENTS_COUNT)
            {
                DXGK_SEGMENTDESCRIPTOR3 *paDesc = pOut->pSegmentDescriptor;
                for (unsigned i = 0; i < VBOXDX_SEGMENTS_COUNT; ++i)
                {
                    VBOXDXSEGMENTDESCRIPTOR desc;
                    vmsvgaDXGetSegmentDescription(pDevExt, i, &desc);
                    paDesc[i].Flags                = desc.Flags;
                    paDesc[i].CpuTranslatedAddress = desc.CpuTranslatedAddress;
                    paDesc[i].Size                 = desc.Size;
                    paDesc[i].CommitLimit          = desc.Size;
                }

                pOut->PagingBufferSegmentId       = 0;
                pOut->PagingBufferSize            = PAGE_SIZE;
                pOut->PagingBufferPrivateDataSize = PAGE_SIZE;
            }
            else
            {
                LOGE("NbSegment %d", pOut->NbSegment);
                Status = STATUS_INVALID_PARAMETER;
            }
            break;
        }
#endif
        LOGF("DXGKQAITYPE_QUERYSEGMENT3 treating as unsupported!\n");
        Status = STATUS_NOT_SUPPORTED;
        break;
    }
    case DXGKQAITYPE_ADAPTERPERFDATA:
    {
        // No need to output log to backend
        LOGI("DXGKQAITYPE_ADAPTERPERFDATA treating as unsupported\n");
        Status = STATUS_NOT_SUPPORTED;
        break;
    }
    default:
        LOGF("unsupported Type (%d)\n", pQueryAdapterInfo->Type);
        Status = STATUS_NOT_SUPPORTED;
        break;
    }

    LOGI("LEAVE, context(0x%p), Status(0x%x)\n", hAdapter, Status);

    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiSetPalette(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_SETPALETTE* pSetPalette
)
{
    RT_NOREF(hAdapter, pSetPalette);

    LOGE("DxgkDdiSetPalette\n");

    /** @todo fixme: implement */

    return STATUS_NOT_IMPLEMENTED;
}

/** Find which area of a 32 bit mouse pointer bitmap is actually used.
 * Zero pixels on the right and the bottom of the bitmap are considered unused.
 *
 * @param pPixels               The bitmap.
 * @param Pitch                 The bitmap scanline size in bytes.
 * @param Width                 The bitmap width.
 * @param Height                The bitmap height.
 * @param piMaxFilledPixel      Where to store the maximum index of non-zero pixel within a scanline.
 * @param piMaxFilledScanline   Where to store the zero based index of the last scanline with non-zero pixels.
 */
static void vboxWddmPointerFindDimensionsColor(void const* pPixels, UINT Pitch, UINT Width, UINT Height,
    LONG* piMaxFilledPixel, LONG* piMaxFilledScanline)
{
    /* Windows always uses the maximum pointer size VBOXWDDM_C_POINTER_MAX_*
     * Exclude zero pixels (which are transparent anyway) from the right and the bottom of the bitmap.
     */
    DWORD const* pdwScanline = (DWORD*)pPixels;
    LONG iMaxFilledScanline = -1;
    LONG iMaxFilledPixel = -1;
    for (ULONG y = 0; y < Height; ++y)
    {
        LONG iLastFilledPixel = -1;
        for (ULONG x = 0; x < Width; ++x)
        {
            if (pdwScanline[x])
                iLastFilledPixel = x;
        }

        iMaxFilledPixel = RT_MAX(iMaxFilledPixel, iLastFilledPixel);

        if (iLastFilledPixel >= 0)
        {
            /* Scanline contains non-zero pixels. */
            iMaxFilledScanline = y;
        }

        pdwScanline = (DWORD*)((UINT8 *)pdwScanline + Pitch);
    }

    *piMaxFilledPixel = iMaxFilledPixel;
    *piMaxFilledScanline = iMaxFilledScanline;
}

/** Adjust the width and the height of the mouse pointer bitmap.
 * See comments in the function for the adjustment criteria.
 *
 * @param iMaxX   The index of the rightmost pixel which we want to keep.
 * @param iMaxY   The index of the bottom-most pixel which we want to keep.
 * @param XHot    The mouse pointer hot spot.
 * @param YHot    The mouse pointer hot spot.
 * @param pWidth  Where to store the bitmap width.
 * @param pHeight Where to store the bitmap height.
 */
static void vboxWddmPointerAdjustDimensions(LONG iMaxX, LONG iMaxY, UINT XHot, UINT YHot,
    ULONG* pWidth, ULONG* pHeight)
{
    /* Both input parameters are zero based indexes, add 1 to get a width and a height. */
    ULONG W = iMaxX + 1;
    ULONG H = iMaxY + 1;

    /* Always include the hotspot point. */
    W = RT_MAX(XHot, W);
    H = RT_MAX(YHot, H);

    /* Align to 8 pixels, because the XOR/AND pointers are aligned like that.
     * The AND mask has one bit per pixel with 8 bits per byte.
     * In case the host can't deal with unaligned data.
     */
    W = RT_ALIGN_T(W, 8, ULONG);
    H = RT_ALIGN_T(H, 8, ULONG);

    /* Do not send bitmaps with zero dimensions. Actually make the min size 32x32. */
    W = RT_MAX(32, W);
    H = RT_MAX(32, H);

    /* Make it square. Some hosts are known to require square pointers. */
    W = RT_MAX(W, H);
    H = W;

    /* Do not exceed the supported size.
     * Actually this should not be necessary because Windows never creates such pointers.
     */
    W = RT_MIN(W, VBOXWDDM_C_POINTER_MAX_WIDTH);
    H = RT_MIN(H, VBOXWDDM_C_POINTER_MAX_HEIGHT);

    *pWidth = W;
    *pHeight = H;
}

#define SET_EDGE_WHITE_COLOR(x) do {  \
    if (x == 0x0) {              \
        x = 0xFFFFFFFF;          \
    }                            \
} while (0)

BOOL vboxWddmPointerCopyColorData(CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape, PVIDEO_POINTER_ATTRIBUTES pPointerAttributes,
    bool fDwordAlignScanlines, bool isMaskedColor)
{
    ULONG srcMaskW, srcMaskH;
    ULONG dstBytesPerLine;
    ULONG x, y;
    BYTE* pSrc, * pDst, bit;

    /* Windows always uses the maximum pointer size VBOXWDDM_C_POINTER_MAX_*
     * Exclude zero pixels (which are transparent anyway) from the right and the bottom of the bitmap.
     */
    LONG iMaxFilledPixel;
    LONG iMaxFilledScanline;
    vboxWddmPointerFindDimensionsColor(pSetPointerShape->pPixels, pSetPointerShape->Pitch,
        pSetPointerShape->Width, pSetPointerShape->Height,
        &iMaxFilledPixel, &iMaxFilledScanline);

    vboxWddmPointerAdjustDimensions(iMaxFilledPixel, iMaxFilledScanline,
        pSetPointerShape->XHot, pSetPointerShape->YHot,
        &srcMaskW, &srcMaskH);

    pPointerAttributes->Width = srcMaskW;
    pPointerAttributes->Height = srcMaskH;
    pPointerAttributes->WidthInBytes = pPointerAttributes->Width * 4;

    /* cnstruct and mask from alpha color channel */
    pSrc = (PBYTE)pSetPointerShape->pPixels;
    pDst = pPointerAttributes->Pixels;
    dstBytesPerLine = (pPointerAttributes->Width + 7) / 8;
    if (fDwordAlignScanlines)
        dstBytesPerLine = RT_ALIGN_T(dstBytesPerLine, 4, ULONG);

    /* sanity check */
    UINT32 cbData = RT_ALIGN_T(dstBytesPerLine * pPointerAttributes->Height, 4, ULONG) +
        pPointerAttributes->Height * pPointerAttributes->WidthInBytes;
    UINT32 cbPointerAttributes = RT_UOFFSETOF_DYN(VIDEO_POINTER_ATTRIBUTES, Pixels[cbData]);
    if (VBOXWDDM_POINTER_ATTRIBUTES_SIZE < cbPointerAttributes)
    {
        LOGE("VBOXWDDM_POINTER_ATTRIBUTES_SIZE(%d) < cbPointerAttributes(%d)\n", VBOXWDDM_POINTER_ATTRIBUTES_SIZE, cbPointerAttributes);
        return FALSE;
    }

    memset(pDst, 0xFF, dstBytesPerLine * pPointerAttributes->Height);
    for (y = 0; y < RT_MIN(pSetPointerShape->Height, pPointerAttributes->Height); ++y)
    {
        for (x = 0, bit = 7; x < RT_MIN(pSetPointerShape->Width, pPointerAttributes->Width); ++x, --bit)
        {
            if (0xFF == bit) bit = 7;

            if (pSrc[y * pSetPointerShape->Pitch + x * 4 + 3] > 0x7F)
            {
                pDst[y * dstBytesPerLine + x / 8] &= ~(1 << bit);
            }
        }
    }

    /* copy 32bpp to XOR DIB, it start in pPointerAttributes->Pixels should be 4bytes aligned */
    pSrc = (BYTE*)pSetPointerShape->pPixels;
    pDst = pPointerAttributes->Pixels + RT_ALIGN_T(dstBytesPerLine * pPointerAttributes->Height, 4, ULONG);
    dstBytesPerLine = pPointerAttributes->Width * 4;
    if (!isMaskedColor) {
        memset(pDst, 0x00, dstBytesPerLine * pPointerAttributes->Height);
    } else {
        memset(pDst, 0xEE, dstBytesPerLine * pPointerAttributes->Height);
    }

    for (y = 0; y < RT_MIN(pSetPointerShape->Height, pPointerAttributes->Height); ++y)
    {
        memcpy(pDst + y * dstBytesPerLine, pSrc + y * pSetPointerShape->Pitch, RT_MIN(dstBytesPerLine, pSetPointerShape->Pitch));
    }

    if (isMaskedColor) {
        LOGI("use maskedcolor cursor\n");
        ULONG *pDstUL = (ULONG *)pDst;
        for (y = 0; y < pPointerAttributes->Height; ++y) {
            for (x = 0; x < pPointerAttributes->Width; ++x) {
                ULONG index = y * pPointerAttributes->Width + x;
                ULONG mask = (pDstUL[index] >> 24) & 0x000000FF;
                ULONG rgbValue = pDstUL[index] & 0x00FFFFFF;
                if (mask == 0x0) {
                    // when mask is 0, use RGB value, set alpha to 0xFF
                    pDstUL[index] = 0xFF000000 + rgbValue;
                } else if (mask == 0xFF) {
                    // when mask is 0xFF, use RGB value XOR with screen value, we just consider white background
                    if (rgbValue == 0) {
                        // for zero rgb vaule, make black pixel to transparent
                        pDstUL[index] = 0x0;
                    } else {
                        // for non-zero rgb value, xor with white background, show with black
                        pDstUL[index] = 0xFF000000;
                    }
                } else if (mask == 0xEE) {
                    pDstUL[index] = 0x0;
                }
                else {
                    LOGE("Invalid MaskedColor mask %x\n", mask);
                }
            }
        }

        // Add white-color edge outside the origin black cursor, make it display clear with dark background
        for (y = 0; y < pPointerAttributes->Height; ++y) {
            for (x = 0; x < pPointerAttributes->Width; ++x) {
                ULONG index = y * pPointerAttributes->Width + x;
                // Has valid cursor data
                if (pDstUL[index] == 0xFF000000) {
                    // Check index value to avoid overflow access
                    if (y > 0) {
                        ULONG upindex = (y - 1) * pPointerAttributes->Width + x;
                        SET_EDGE_WHITE_COLOR(pDstUL[upindex]);
                    }
                    if (x > 0) {
                        ULONG leftindex = y * pPointerAttributes->Width + (x - 1);
                        SET_EDGE_WHITE_COLOR(pDstUL[leftindex]);
                    }
                    if (y < pPointerAttributes->Height - 1) {
                        ULONG downindex = (y + 1) * pPointerAttributes->Width + x;
                        SET_EDGE_WHITE_COLOR(pDstUL[downindex]);
                    }
                    if (x < pPointerAttributes->Width - 1) {
                        ULONG rightindex = y * pPointerAttributes->Width + (x + 1);
                        SET_EDGE_WHITE_COLOR(pDstUL[rightindex]);
                    }
                }
            }
        }
    }

    return TRUE;
}

/** Find which area of a 1 bit AND/XOR mask bitmap is actually used, i.e. filled with actual data.
 * For the AND mask the bytes with a value 0xff on the right and the bottom of the bitmap are considered unused.
 * For the XOR mask the blank value is 0x00.
 *
 * @param pPixels             The 1bit bitmap.
 * @param Pitch               The 1bit bitmap scanline size in bytes.
 * @param Width               The bitmap width.
 * @param Height              The bitmap height.
 * @param Blank               The value of the unused bytes in the supplied bitmap.
 * @param piMaxFilledPixel    Where to store the maximum index of a filled pixel within a scanline.
 * @param piMaxFilledScanline Where to store the zero based index of the last scanline with filled pixels.
 */
static void vboxWddmPointerFindDimensionsMono(void const* pPixels, UINT Pitch, UINT Width, UINT Height, BYTE Blank,
    LONG* piMaxFilledPixel, LONG* piMaxFilledScanline)
{
    /* Windows always uses the maximum pointer size VBOXWDDM_C_POINTER_MAX_*
     * Exclude the blank pixels (which are transparent anyway) from the right and the bottom of the bitmap.
     */
    BYTE const* pbScanline = (BYTE*)pPixels;
    LONG iMaxFilledScanline = -1;
    LONG iMaxFilledByte = -1;
    for (ULONG y = 0; y < Height; ++y)
    {
        LONG iLastFilledByte = -1;
        for (ULONG x = 0; x < Width / 8; ++x)
        {
            if (pbScanline[x] != Blank)
                iLastFilledByte = x;
        }

        iMaxFilledByte = RT_MAX(iMaxFilledByte, iLastFilledByte);

        if (iLastFilledByte >= 0)
        {
            /* Scanline contains filled pixels. */
            iMaxFilledScanline = y;
        }

        pbScanline += Pitch;
    }

    *piMaxFilledPixel = iMaxFilledByte * 8;
    *piMaxFilledScanline = iMaxFilledScanline;
}

BOOL vboxWddmPointerCopyMonoData(CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape, PVIDEO_POINTER_ATTRIBUTES pPointerAttributes,
    bool fDwordAlignScanlines)
{
    ULONG srcMaskW, srcMaskH;
    ULONG dstBytesPerLine;
    ULONG x, y;
    BYTE* pSrc, * pDst, bit;

    /* Windows always uses the maximum pointer size VBOXWDDM_C_POINTER_MAX_*
     * Exclude unused pixels (which are transparent anyway) from the right and the bottom of the bitmap.
     */
    LONG iMaxFilledPixelAND;
    LONG iMaxFilledScanlineAND;
    vboxWddmPointerFindDimensionsMono(pSetPointerShape->pPixels, pSetPointerShape->Pitch,
        pSetPointerShape->Width, pSetPointerShape->Height, 0xff,
        &iMaxFilledPixelAND, &iMaxFilledScanlineAND);

    LONG iMaxFilledPixelXOR;
    LONG iMaxFilledScanlineXOR;
    vboxWddmPointerFindDimensionsMono((BYTE*)pSetPointerShape->pPixels + pSetPointerShape->Height * pSetPointerShape->Pitch,
        pSetPointerShape->Pitch,
        pSetPointerShape->Width, pSetPointerShape->Height, 0x00,
        &iMaxFilledPixelXOR, &iMaxFilledScanlineXOR);

    LONG iMaxFilledPixel = RT_MAX(iMaxFilledPixelAND, iMaxFilledPixelXOR);
    LONG iMaxFilledScanline = RT_MAX(iMaxFilledScanlineAND, iMaxFilledScanlineXOR);

    vboxWddmPointerAdjustDimensions(iMaxFilledPixel, iMaxFilledScanline,
        pSetPointerShape->XHot, pSetPointerShape->YHot,
        &srcMaskW, &srcMaskH);

    pPointerAttributes->Width = srcMaskW;
    pPointerAttributes->Height = srcMaskH;
    pPointerAttributes->WidthInBytes = pPointerAttributes->Width * 4;

    /* copy AND mask */
    pSrc = (PBYTE)pSetPointerShape->pPixels;
    pDst = pPointerAttributes->Pixels;
    dstBytesPerLine = (pPointerAttributes->Width + 7) / 8;
    if (fDwordAlignScanlines)
        dstBytesPerLine = RT_ALIGN_T(dstBytesPerLine, 4, ULONG);

    for (y = 0; y < pPointerAttributes->Height; ++y)
    {
        memcpy(pDst + y * dstBytesPerLine, pSrc + y * pSetPointerShape->Pitch, dstBytesPerLine);
    }

    /* convert XOR mask to RGB0 DIB, it start in pPointerAttributes->Pixels should be 4bytes aligned */
    pSrc = (BYTE*)pSetPointerShape->pPixels + pSetPointerShape->Height * pSetPointerShape->Pitch;
    pDst = pPointerAttributes->Pixels + RT_ALIGN_T(dstBytesPerLine * pPointerAttributes->Height, 4, ULONG);
    dstBytesPerLine = pPointerAttributes->Width * 4;

    for (y = 0; y < pPointerAttributes->Height; ++y)
    {
        for (x = 0, bit = 7; x < pPointerAttributes->Width; ++x, --bit)
        {
            if (0xFF == bit) bit = 7;

            *(ULONG*)&pDst[y * dstBytesPerLine + x * 4] = (pSrc[y * pSetPointerShape->Pitch + x / 8] & (1 << bit)) ? 0x00FFFFFF : 0;
        }
    }

    return TRUE;
}

static BOOLEAN vboxWddmPointerShapeToAttributes(CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape, PVBOXWDDM_POINTER_INFO pPointerInfo,
    bool fDwordAlignScanlines)
{
    PVIDEO_POINTER_ATTRIBUTES pPointerAttributes = &pPointerInfo->Attributes.data;
    /* pPointerAttributes maintains the visibility state, clear all except visibility */
    pPointerAttributes->Enable &= VBOX_MOUSE_POINTER_VISIBLE;

    if (pSetPointerShape->Flags.Color | pSetPointerShape->Flags.MaskedColor)
    {
        bool isMaskedColor = pSetPointerShape->Flags.MaskedColor != 0;
        if (vboxWddmPointerCopyColorData(pSetPointerShape, pPointerAttributes, fDwordAlignScanlines, isMaskedColor))
        {
            pPointerAttributes->Flags = VIDEO_MODE_COLOR_POINTER;
            pPointerAttributes->Enable |= VBOX_MOUSE_POINTER_ALPHA;
        }
        else
        {
            LOGE("vboxWddmPointerCopyColorData failed\n");
            return FALSE;
        }

    }
    else if (pSetPointerShape->Flags.Monochrome)
    {
        if (vboxWddmPointerCopyMonoData(pSetPointerShape, pPointerAttributes, fDwordAlignScanlines))
        {
            pPointerAttributes->Flags = VIDEO_MODE_MONO_POINTER;
        }
        else
        {
            LOGE("vboxWddmPointerCopyMonoData failed\n");
            return FALSE;
        }
    }
    else
    {
        LOGE("unsupported pointer type Flags.Value(0x%x)\n", pSetPointerShape->Flags.Value);
        return FALSE;
    }

    pPointerAttributes->Enable |= VBOX_MOUSE_POINTER_SHAPE;

    /*
     * The hot spot coordinates and alpha flag will be encoded in the pPointerAttributes::Enable field.
     * High word will contain hot spot info and low word - flags.
     */
    pPointerAttributes->Enable |= (pSetPointerShape->YHot & 0xFF) << 24;
    pPointerAttributes->Enable |= (pSetPointerShape->XHot & 0xFF) << 16;

    return TRUE;
}

void vboxWddmSendTransparentHostCursor(PVBOXMP_DEVEXT pDevExt) {
    // for hw cursor, just support alpha cursor and its width/height small than 64
    if (ASMAtomicReadU32(&pDevExt->pGa->u32UseSoftCursor) != 0) {
        // already in soft cursor, no need to send cmd to host
        return;
    }
    ASMAtomicWriteU32(&pDevExt->pGa->u32UseSoftCursor, 1);

    // send an full transrent cursor pic to host to hide host cursor
    // just with small cursor size with 32*32
    NTSTATUS Status = STATUS_SUCCESS;
    UINT32 cWidth = 32;
    UINT32 cHeight = 32;
    UINT32 xHot = 0;
    UINT32 yHot = 0;
    UINT32 cbAndMask = ((((cWidth + 7) / 8) + 3) & ~3) * cHeight;
    UINT32 cbXorMask = cWidth * 4 * cHeight;
    void* pvImage = pDevExt->pGa->pvImageTransCursor;
    Status = GaDefineAlphaCursor(pDevExt->pGa,
                                         xHot,
                                         yHot,
                                         cWidth,
                                         cHeight,
                                         pvImage,
                                         cbXorMask);
}

bool vboxWddmUpdatePointerShape(PVBOXMP_DEVEXT pDevExt, PVIDEO_POINTER_ATTRIBUTES pAttrs, UINT32 cbLength)
{
    RT_NOREF(cbLength);
    NTSTATUS Status = STATUS_SUCCESS;

    /** @todo Get rid of the unnecesary en-/decode procedure (XPDM legacy). */
    UINT32 fFlags = pAttrs->Enable & 0x0000FFFF;
    UINT32 xHot = (pAttrs->Enable >> 16) & 0xFF;
    UINT32 yHot = (pAttrs->Enable >> 24) & 0xFF;
    UINT32 cWidth = pAttrs->Width;
    UINT32 cHeight = pAttrs->Height;
    UINT32 cbAndMask = 0;
    UINT32 cbXorMask = 0;

    if (fFlags & VBOX_MOUSE_POINTER_SHAPE)
    {
        if (cWidth > MAX_HW_CURSOR_WIDTH || cHeight > MAX_HW_CURSOR_HEIGHT) {
            // the cursor size is too large, use soft cursor
            vboxWddmSendTransparentHostCursor(pDevExt);
            return FALSE;
        }

        /* Size of the pointer data: sizeof(AND mask) + sizeof(XOR mask) */
        /* "Each scanline is padded to a 32-bit boundary." */
        cbAndMask = ((((cWidth + 7) / 8) + 3) & ~3) * cHeight;
        cbXorMask = cWidth * 4 * cHeight;

        /* Send the shape to the host. */
        if (fFlags & VBOX_MOUSE_POINTER_ALPHA)
        {
            void const* pvImage = &pAttrs->Pixels[cbAndMask];
            Status = GaDefineAlphaCursor(pDevExt->pGa,
                                         xHot,
                                         yHot,
                                         cWidth,
                                         cHeight,
                                         pvImage,
                                         cbXorMask);
        }
        else
        {
            // Use hw cursor for mono mouse
            UINT32 u32AndMaskDepth = 1;
            UINT32 u32XorMaskDepth = 32;

            void const* pvAndMask = &pAttrs->Pixels[0];
            void const* pvXorMask = &pAttrs->Pixels[cbAndMask];
            Status = GaDefineCursor(pDevExt->pGa,
                                    xHot,
                                    yHot,
                                    cWidth,
                                    cHeight,
                                    u32AndMaskDepth,
                                    u32XorMaskDepth,
                                    pvAndMask,
                                    cbAndMask,
                                    pvXorMask,
                                    cbXorMask);

        }
        // use hw cursor
        ASMAtomicWriteU32(&pDevExt->pGa->u32UseSoftCursor, 0);
    }

    /** @todo Hack: Use the legacy interface to handle visibility.
        * Eventually the VMSVGA WDDM driver should use the SVGA_FIFO_CURSOR_* interface.
        */
    // TODO: update SVGA FIFO POINTER fields

    return Status == STATUS_SUCCESS;
}

static void vboxWddmHostPointerEnable(PVBOXMP_DEVEXT pDevExt, BOOLEAN fEnable)
{
    VIDEO_POINTER_ATTRIBUTES PointerAttributes;
    memset(&PointerAttributes, 0, sizeof(PointerAttributes));
    if (fEnable)
    {
        PointerAttributes.Enable = VBOX_MOUSE_POINTER_VISIBLE;
    }
    vboxWddmUpdatePointerShape(pDevExt, &PointerAttributes, sizeof(PointerAttributes));
}

NTSTATUS
APIENTRY
DxgkDdiSetPointerPosition(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition)
{
    LOGI("ENTER==>%s, hAdapter(0x%x)\n", __FUNCTION__, hAdapter);

    /* mouse integration is ON */
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    PVBOXWDDM_POINTER_INFO pPointerInfo = &pDevExt->aSources[pSetPointerPosition->VidPnSourceId].PointerInfo;
    PVBOXWDDM_GLOBAL_POINTER_INFO pGlobalPointerInfo = &pDevExt->PointerInfo;
    PVIDEO_POINTER_ATTRIBUTES pPointerAttributes = &pPointerInfo->Attributes.data;
    BOOLEAN fScreenVisState = !!(pPointerAttributes->Enable & VBOX_MOUSE_POINTER_VISIBLE);
    BOOLEAN fVisStateChanged = FALSE;
    BOOLEAN fScreenChanged = pGlobalPointerInfo->iLastReportedScreen != pSetPointerPosition->VidPnSourceId;

    if (pSetPointerPosition->Flags.Visible)
    {
        pPointerAttributes->Enable |= VBOX_MOUSE_POINTER_VISIBLE;
        if (!fScreenVisState)
        {
            fVisStateChanged = TRUE;
            // Translate from non-visible to visible, resend hw cursor data to host, redraw hw cursor in host
            vboxWddmUpdatePointerShape(pDevExt, &pPointerInfo->Attributes.data, VBOXWDDM_POINTER_ATTRIBUTES_SIZE);
        }
        ASMAtomicWriteU32(&pPointerInfo->hasPendingNoVisible, 0);
    }
    else
    {
        // In 100-nanosecode units
        ULONG64 QpcTsIgnored;
        UINT64 noVisibleTime = KeQueryInterruptTimePrecise(&QpcTsIgnored);
        // if no visible duration > 1 sec, send transparent cursor, otherwise do nothing
        if (noVisibleTime - pPointerInfo->lastNoVisibleTime > 10000000) {
            pPointerAttributes->Enable &= ~VBOX_MOUSE_POINTER_VISIBLE;
            // Cancel pending set shape of hw cursor because it will change into soft cursor
            ASMAtomicWriteU32(&pPointerInfo->hasPendingSetShape, 0);
            if (fScreenVisState)
            {
                fVisStateChanged = TRUE;
                // It shoud use guest cursor in vm, thus we need hide host cursor
                vboxWddmSendTransparentHostCursor(pDevExt);
            }
            ASMAtomicWriteU32(&pPointerInfo->hasPendingNoVisible, 0);
        } else {
            ASMAtomicWriteU32(&pPointerInfo->hasPendingNoVisible, 1);
        }
        pPointerInfo->lastNoVisibleTime = noVisibleTime;
    }

    pGlobalPointerInfo->iLastReportedScreen = pSetPointerPosition->VidPnSourceId;

    // Right now, we do not support multiple screens, thus remove screen change logic

    LOGI("LEAVE<==%s, hAdapter(0x%x)\n", __FUNCTION__, hAdapter);

    return STATUS_SUCCESS;
}

typedef NTSTATUS(*QUERY_INFO_PROCESS) (
    __in HANDLE ProcessHandle,
    __in PROCESSINFOCLASS ProcessInformationClass,
    __out_bcount(ProcessInformationLength) PVOID ProcessInformation,
    __in ULONG ProcessInformationLength,
    __out_opt PULONG ReturnLength
    );

QUERY_INFO_PROCESS ZwQueryInformationProcess = NULL;

NTSTATUS GetProcessImageName(HANDLE ProcessHandle, PUNICODE_STRING* ProcessImageName)
{
    NTSTATUS status = STATUS_ACCESS_DENIED;
    ULONG returnedLength = 0;
    PVOID buffer = NULL;

    if (ZwQueryInformationProcess == NULL)
    {
        UNICODE_STRING routineName;
        RtlInitUnicodeString(&routineName, L"ZwQueryInformationProcess");
        ZwQueryInformationProcess = (QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName);
        if (NULL == ZwQueryInformationProcess)
            return STATUS_INSUFFICIENT_RESOURCES;
    }

    status = ZwQueryInformationProcess(ProcessHandle, ProcessImageFileName, NULL, 0, &returnedLength);
    if (STATUS_INFO_LENGTH_MISMATCH != status)
        return status;

    buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, returnedLength, SVGA_MEMTAG);
    if (NULL == buffer)
        return STATUS_INSUFFICIENT_RESOURCES;

    status = ZwQueryInformationProcess(ProcessHandle, ProcessImageFileName, buffer, returnedLength, &returnedLength);
    if (NT_SUCCESS(status)) {
        PUNICODE_STRING pstr = (PUNICODE_STRING)buffer;
        if (pstr->Length > 0) {
            *ProcessImageName = (PUNICODE_STRING)buffer;
            return status;
        }
        status = STATUS_INVALID_PARAMETER;
    }

    ExFreePoolWithTag(buffer, SVGA_MEMTAG);
    return status;
}

BOOLEAN GetCurrentProcessName(PUNICODE_STRING* pImageFileName)
{
    HANDLE hProcessHandle = NULL;
    NTSTATUS status = STATUS_ACCESS_DENIED;
    PEPROCESS eProcess = NULL;
    BOOLEAN bRes = FALSE;
    HANDLE lProcID = NULL;

    do
    {
        lProcID = PsGetCurrentProcessId();
        if (lProcID == NULL)
            break;

        status = PsLookupProcessByProcessId(lProcID, &eProcess);
        if ((!NT_SUCCESS(status)) || (!eProcess))
            break;

        status = ObOpenObjectByPointer(eProcess, OBJ_KERNEL_HANDLE, NULL, 0, 0, KernelMode, &hProcessHandle);
        if ((!NT_SUCCESS(status)) || (!hProcessHandle))
            break;

        status = GetProcessImageName(hProcessHandle, pImageFileName);
        if (!NT_SUCCESS(status))
            break;

        PUNICODE_STRING ImgPath = *pImageFileName;
        LONG slashIdx = ImgPath->Length / sizeof(WCHAR) - 1;
        for (; slashIdx >= 0; slashIdx--)
        {
            if (ImgPath->Buffer[slashIdx] == L'\\')
                break;
        }
        ImgPath->Buffer += slashIdx + 1;
        ImgPath->Length -= (slashIdx + 1) * sizeof(WCHAR);
        ImgPath->MaximumLength -= (slashIdx + 1) * sizeof(WCHAR);

        if (ImgPath->Length == 0)
        {
            ExFreePoolWithTag(ImgPath, SVGA_MEMTAG);
            break;
        }

        bRes = TRUE;
    } while (0);

    if (hProcessHandle)
        ZwClose(hProcessHandle);
    if (eProcess)
        ObDereferenceObject(eProcess);

    return bRes;
}

BOOLEAN IsAppHwMonoCursorSupported(PVBOXMP_DEVEXT pDevExt)
{
    PAGED_CODE();

    PUNICODE_STRING currentProcessName;
    if (!GetCurrentProcessName(&currentProcessName))
        return TRUE;

    ULONG i = 0;
    for (; i < pDevExt->HwMonoCursorUnsupportedApps.AppCount; i++)
    {
        UNICODE_STRING ListedApp;
        RtlInitUnicodeString(&ListedApp, pDevExt->HwMonoCursorUnsupportedApps.AppNames[i]);
        if (!RtlCompareUnicodeString(currentProcessName, &ListedApp, TRUE))
            break;
    }
    ExFreePoolWithTag(currentProcessName, SVGA_MEMTAG);

    return (i == pDevExt->HwMonoCursorUnsupportedApps.AppCount);
}

NTSTATUS
APIENTRY
DxgkDdiSetPointerShape(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape)
{
    LOGI("ENTER ==> % s, hAdapter(0x % x)\n", __FUNCTION__, hAdapter);

    NTSTATUS Status = STATUS_NOT_SUPPORTED;

    /* mouse integration is ON */
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    PVBOXWDDM_POINTER_INFO pPointerInfo = &pDevExt->aSources[pSetPointerShape->VidPnSourceId].PointerInfo;
    bool const fDwordAlignScanlines = pDevExt->enmHwType != VBOXVIDEO_HWTYPE_VBOX;

    if (pSetPointerShape->Flags.Monochrome && !IsAppHwMonoCursorSupported(pDevExt))
        return STATUS_SUCCESS;

    // In 100-nanosecode units
    ULONG64 QpcTsIgnored;
    UINT64 setShapeTime = KeQueryInterruptTimePrecise(&QpcTsIgnored);

    /** @todo to avoid extra data copy and extra heap allocation,
        *  need to maintain the pre-allocated HGSMI buffer and convert the data directly to it */
    if (vboxWddmPointerShapeToAttributes(pSetPointerShape, pPointerInfo, fDwordAlignScanlines))
    {
        pDevExt->PointerInfo.iLastReportedScreen = pSetPointerShape->VidPnSourceId;

        if (setShapeTime - pPointerInfo->lastSetShapeTime < 1000000) {
            LOGI("no set pointer shape\n");
            ASMAtomicWriteU32(&pPointerInfo->hasPendingSetShape, 1);
            return STATUS_SUCCESS;
        }
        ASMAtomicWriteU32(&pPointerInfo->hasPendingSetShape, 0);
        pPointerInfo->lastSetShapeTime = setShapeTime;

        if (vboxWddmUpdatePointerShape(pDevExt, &pPointerInfo->Attributes.data, VBOXWDDM_POINTER_ATTRIBUTES_SIZE))
            Status = STATUS_SUCCESS;
        else
        {
            return STATUS_NOT_SUPPORTED;
        }
    }

    LOGI("LEAVE<==%s, hAdapter(0x%x)\n", __FUNCTION__, hAdapter);

    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiEscape(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_ESCAPE* pEscape)
{
    NTSTATUS Status = STATUS_NOT_SUPPORTED;
    const VBOXDISPIFESCAPE *pEscapeHdr = (VBOXDISPIFESCAPE *)pEscape->pPrivateDriverData;

    if (pEscape->PrivateDriverDataSize < sizeof(VBOXDISPIFESCAPE)) {
        LOGE("pEscape->PrivateDriverDataSize(%d) < (%d)\n", pEscape->PrivateDriverDataSize, sizeof (VBOXDISPIFESCAPE));
        return STATUS_INVALID_PARAMETER;
    }
    if (pEscapeHdr->escapeCode != VBOXESC_GALOGTOKMD)
        LOGI("hAdapter(0x%p) escape code (0x%x)\n", hAdapter, pEscapeHdr->escapeCode);

    switch (pEscapeHdr->escapeCode) {
    case VBOXESC_SETVISIBLEREGION:
    case VBOXESC_ISVRDPACTIVE:
    case VBOXESC_CONFIGURETARGETS:
    case VBOXESC_SETALLOCHOSTID:
    case VBOXESC_ISANYX:
    case VBOXESC_UPDATEMODES:
    case VBOXESC_TARGET_CONNECTIVITY:
    case VBOXESC_DBGPRINT:
    case VBOXESC_DBGDUMPBUF:
    case VBOXESC_GUEST_DISPLAYCHANGED:
        Status = STATUS_NOT_SUPPORTED;
        break;
    default:
        Status = GaDxgkDdiEscape(hAdapter, pEscape);
        if (!NT_SUCCESS(Status)) {
            LOGE("unsupported escape code (0x%x)\n", pEscapeHdr->escapeCode);
        }
        break;
    }

    return Status;
}

NTSTATUS DxgkDdiIsSupportedVidPn(CONST HANDLE  hAdapter,
    OUT DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPnArg)
{
    /* The DxgkDdiIsSupportedVidPn should be made pageable. */
    PAGED_CODE();

    LOGI("==> hAdapter(0x%p)\n",  hAdapter);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    NTSTATUS Status = VBoxVidPnIsSupported(pDevExt, pIsSupportedVidPnArg->hDesiredVidPn,
                                           &pIsSupportedVidPnArg->IsVidPnSupported);
    if (!NT_SUCCESS(Status)) {
        LOGE("VBoxVidPnIsSupported failed Status(0x%x)\n", Status);
    }

    LOGI("<== hAdapter(0x%p), isSupported(%d), Status(0x%x)\n",  hAdapter, pIsSupportedVidPnArg->IsVidPnSupported,  Status);

    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiRecommendFunctionalVidPn(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST  pRecommendFunctionalVidPnArg)
{
    /* The DxgkDdiRecommendFunctionalVidPn should be made pageable. */
    PAGED_CODE();

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    if (pRecommendFunctionalVidPnArg->PrivateDriverDataSize != sizeof (VBOXWDDM_RECOMMENDVIDPN)) {
        LOGE("invalid size\n");
        return STATUS_INVALID_PARAMETER;
    }

    VBOXWDDM_RECOMMENDVIDPN *pData = (VBOXWDDM_RECOMMENDVIDPN*)pRecommendFunctionalVidPnArg->pPrivateDriverData;

    NTSTATUS Status = VBoxVidPnRecommendFunctional(pDevExt, pRecommendFunctionalVidPnArg->hRecommendedFunctionalVidPn, pData);
    if (!NT_SUCCESS(Status)) {
        LOGE("VBoxVidPnRecommendFunctional failed %#x\n", Status);
        return Status;
    }

    LOGI("LEAVE, status(0x%x), context(0x%p)\n", Status, hAdapter);

    return STATUS_SUCCESS;
}

NTSTATUS
APIENTRY
DxgkDdiEnumVidPnCofuncModality(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST  pEnumCofuncModalityArg)
{
    /* The DxgkDdiEnumVidPnCofuncModality function should be made pageable. */
    PAGED_CODE();
    LOGI("==> context(0x%x)\n", hAdapter);
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    NTSTATUS Status = VBoxVidPnCofuncModality(pDevExt, pEnumCofuncModalityArg->hConstrainingVidPn,
                        pEnumCofuncModalityArg->EnumPivotType, &pEnumCofuncModalityArg->EnumPivot);
    if (!NT_SUCCESS(Status)) {
        LOGE("VBoxVidPnCofuncModality failed Status(%#x)\n", Status);
        return Status;
    }
    LOGI("<== context(0x%x)\n", hAdapter);
    return STATUS_SUCCESS;
}

NTSTATUS
APIENTRY
DxgkDdiSetVidPnSourceVisibility(
    CONST HANDLE hAdapter,
    CONST DXGKARG_SETVIDPNSOURCEVISIBILITY *pSetVidPnSourceVisibility)
{
    /* DxgkDdiSetVidPnSourceVisibility should be made pageable. */
    PAGED_CODE();

    LOGI("hAdapter %p\n", hAdapter);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    if ((UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays <=
        pSetVidPnSourceVisibility->VidPnSourceId) {
        LOGE("invalid VidPnSourceId (%d), for displays(%d)\n",
             pSetVidPnSourceVisibility->VidPnSourceId,
             VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
        return STATUS_INVALID_PARAMETER;
    }

    if (pDevExt->enmHwType != VBOXVIDEO_HWTYPE_VMSVGA) {
        LOGE("Invalid state: just support vmsvga hwtype\n");
        return STATUS_INVALID_PARAMETER;
    }

    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[pSetVidPnSourceVisibility->VidPnSourceId];
    PVBOXWDDM_ALLOCATION pAllocation = pSource->pPrimaryAllocation;
    if (pAllocation) {
        pAllocation->bVisible = pSetVidPnSourceVisibility->Visible;
    }

    if (pSource->bVisible != pSetVidPnSourceVisibility->Visible) {
        pSource->bVisible = pSetVidPnSourceVisibility->Visible;
    }

    if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA) {
        GaVidPnSourceCheckPos(pDevExt, pSetVidPnSourceVisibility->VidPnSourceId);
    }

    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiCommitVidPn(
    CONST HANDLE hAdapter,
    CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPnArg)
{
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    NTSTATUS Status;

    VBOXWDDM_SOURCE *paSources = (VBOXWDDM_SOURCE*)RTMemAlloc(sizeof (VBOXWDDM_SOURCE) *
                                            VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
    if (!paSources) {
        LOGE("RTMemAlloc failed\n");
        return STATUS_NO_MEMORY;
    }

    VBOXWDDM_TARGET *paTargets = (VBOXWDDM_TARGET*)RTMemAlloc(sizeof (VBOXWDDM_TARGET) *
                                            VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
    if (!paTargets) {
        LOGE("RTMemAlloc failed\n");
        RTMemFree(paSources);
        return STATUS_NO_MEMORY;
    }

    VBoxVidPnSourcesInit(paSources, VBoxCommonFromDeviceExt(pDevExt)->cDisplays,
                         VBOXWDDM_HGSYNC_F_SYNCED_ALL);
    VBoxVidPnTargetsInit(paTargets, VBoxCommonFromDeviceExt(pDevExt)->cDisplays,
                         VBOXWDDM_HGSYNC_F_SYNCED_ALL);

    VBoxVidPnSourcesCopy(paSources, pDevExt->aSources, VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
    VBoxVidPnTargetsCopy(paTargets, pDevExt->aTargets, VBoxCommonFromDeviceExt(pDevExt)->cDisplays);

    do {
        const DXGK_VIDPN_INTERFACE* pVidPnInterface = NULL;
        Status = pDevExt->u.primary.DxgkInterface.DxgkCbQueryVidPnInterface(
                    pCommitVidPnArg->hFunctionalVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1,
                    &pVidPnInterface);
        if (!NT_SUCCESS(Status)) {
            LOGE("DxgkCbQueryVidPnInterface failed Status 0x%x\n", Status);
            break;
        }

        if (pCommitVidPnArg->AffectedVidPnSourceId != D3DDDI_ID_ALL) {
            Status = VBoxVidPnCommitSourceModeForSrcId(
                    pDevExt,
                    pCommitVidPnArg->hFunctionalVidPn, pVidPnInterface,
                    (PVBOXWDDM_ALLOCATION)pCommitVidPnArg->hPrimaryAllocation,
                    pCommitVidPnArg->AffectedVidPnSourceId, paSources, paTargets,
                    pCommitVidPnArg->Flags.PathPowerTransition);
            if (!NT_SUCCESS(Status)) {
                LOGE("VBoxVidPnCommitSourceModeForSrcId for current VidPn failed Status 0x%x\n", Status);
                break;
            }
        } else {
            Status = VBoxVidPnCommitAll(pDevExt, pCommitVidPnArg->hFunctionalVidPn, pVidPnInterface,
                    (PVBOXWDDM_ALLOCATION)pCommitVidPnArg->hPrimaryAllocation,
                    paSources, paTargets);
            if (!NT_SUCCESS(Status)) {
                LOGE("VBoxVidPnCommitAll for current VidPn failed Status 0x%x\n", Status);
                break;
            }
        }

        VBoxVidPnSourcesCopy(pDevExt->aSources, paSources, VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
        VBoxVidPnTargetsCopy(pDevExt->aTargets, paTargets, VBoxCommonFromDeviceExt(pDevExt)->cDisplays);

        VBoxDumpSourceTargetArrays(paSources, paTargets, VBoxCommonFromDeviceExt(pDevExt)->cDisplays);

        if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA) {
            for (int i = 0; i < VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i) {
                VBOXWDDM_SOURCE *pSource = &pDevExt->aSources[i];

                LOGI("Source [%d]: visible %d, blanked %d\n", i,
                     pSource->bVisible, pSource->bBlankedByPowerOff);

                /* Update positions of all screens. */
                vboxWddmDisplaySettingsQueryPos(pDevExt, i, &pSource->VScreenPos);

                GaVidPnSourceReport(pDevExt, pSource);
            }

            for (int i = 0; i < VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i) {
                VBOXWDDM_TARGET *pTarget = &pDevExt->aTargets[i];
                if (pTarget->VidPnSourceId != D3DDDI_ID_UNINITIALIZED) {
                    continue;
                }

                LOGI("Target [%d]: blanked %d\n", i, pTarget->fBlankedByPowerOff);

                if (pTarget->fBlankedByPowerOff) {
                    GaScreenDefine(pDevExt->pGa, 0, pTarget->u32Id, 0, 0, 0, 0, true);
                } else {
                    GaScreenDestroy(pDevExt->pGa, pTarget->u32Id);
                }
            }

            break;
        }
    } while (0);

    RTMemFree(paSources);
    RTMemFree(paTargets);

    LOGI("LEAVE, status(0x%x), hAdapter(0x%p)\n", Status, hAdapter);

    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiUpdateActiveVidPnPresentPath(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST  pUpdateActiveVidPnPresentPathArg)
{
    RT_NOREF(pUpdateActiveVidPnPresentPathArg);

    LOGI("hAdapter(0x%p)\n", hAdapter);

    return STATUS_SUCCESS;
}

NTSTATUS
APIENTRY
DxgkDdiRecommendMonitorModes(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_RECOMMENDMONITORMODES* CONST  pRecommendMonitorModesArg)
{
    LOGI("ENTER==> hAdapter(0x%p)\n", hAdapter);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    NTSTATUS Status = VBoxVidPnRecommendMonitorModes(pDevExt,
                        pRecommendMonitorModesArg->VideoPresentTargetId,
                        pRecommendMonitorModesArg->hMonitorSourceModeSet,
                        pRecommendMonitorModesArg->pMonitorSourceModeSetInterface);
    if (!NT_SUCCESS(Status)) {
        LOGE("VBoxVidPnRecommendMonitorModes failed 0x%x\n", Status);
        return Status;
    }

    LOGI("LEAVE<== hAdapter(0x%p)\n", hAdapter);

    return STATUS_SUCCESS;
}

static NTSTATUS APIENTRY DxgkDdiQueryVidPnHWCapability(
        _In_ const HANDLE hAdapter,
        _Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY *pVidPnHWCaps)
{
    LOGI("ENTER==> hAdapter(0x%p)\n", hAdapter);
    pVidPnHWCaps->VidPnHWCaps.DriverRotation = 0;
    pVidPnHWCaps->VidPnHWCaps.DriverScaling = 0;
    pVidPnHWCaps->VidPnHWCaps.DriverCloning = 0;
    pVidPnHWCaps->VidPnHWCaps.DriverColorConvert = 0;
    pVidPnHWCaps->VidPnHWCaps.DriverLinkedAdapaterOutput = 0;
    pVidPnHWCaps->VidPnHWCaps.DriverRemoteDisplay = 0;
    LOGI("LEAVE<== hAdapter(0x%p)\n", hAdapter);

    return STATUS_SUCCESS;
}

NTSTATUS
APIENTRY
DxgkDdiSetVidPnSourceAddress(
    CONST HANDLE hAdapter,
    CONST DXGKARG_SETVIDPNSOURCEADDRESS *pSetVidPnSourceAddress)
{
    /* The DxgkDdiSetVidPnSourceAddress function should be made pageable. */
    PAGED_CODE();

    LOGI("ENTER==> context(0x%p)\n", hAdapter);
    LOGI("id %d, seg %d, addr 0x%lx, hAllocation %p, ctx cnt %d, f 0x%x\n",
         pSetVidPnSourceAddress->VidPnSourceId,
         pSetVidPnSourceAddress->PrimarySegment,
         pSetVidPnSourceAddress->PrimaryAddress.QuadPart,
         pSetVidPnSourceAddress->hAllocation,
         pSetVidPnSourceAddress->ContextCount,
         pSetVidPnSourceAddress->Flags.Value);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    if ((UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays <=
        pSetVidPnSourceAddress->VidPnSourceId) {
        LOGF("invalid VidPnSourceId (%d), for displays(%d)\n",
             pSetVidPnSourceAddress->VidPnSourceId,
             VBoxCommonFromDeviceExt(pDevExt)->cDisplays);
        return STATUS_INVALID_PARAMETER;
    }

    if (pDevExt->enmHwType != VBOXVIDEO_HWTYPE_VMSVGA) {
        LOGF("invalid hw type %d, just support VMSVGA\n", pDevExt->enmHwType);
        return STATUS_INVALID_PARAMETER;
    }

    PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[pSetVidPnSourceAddress->VidPnSourceId];

    /*
     * Update the source VRAM address.
     */
    PVBOXWDDM_ALLOCATION pAllocation;

    if (pSetVidPnSourceAddress->hAllocation) {
        pAllocation = (PVBOXWDDM_ALLOCATION)pSetVidPnSourceAddress->hAllocation;
        vboxWddmAssignPrimary(pSource, pAllocation, pSetVidPnSourceAddress->VidPnSourceId);
    } else {
        pAllocation = pSource->pPrimaryAllocation;
    }

    if (pAllocation) {
        vboxWddmAddrSetVram(&pAllocation->AllocData.Addr,
            pSetVidPnSourceAddress->PrimarySegment,
            (VBOXVIDEOOFFSET)pSetVidPnSourceAddress->PrimaryAddress.QuadPart);
    }

    if (g_VBoxDisplayOnly && !pAllocation) {
        /* the VRAM here is an absolute address, nto an offset!
         * convert to offset since all internal VBox functionality is offset-based */
        vboxWddmAddrSetVram(&pSource->AllocData.Addr,
            pSetVidPnSourceAddress->PrimarySegment,
            vboxWddmVramAddrToOffset(pDevExt, pSetVidPnSourceAddress->PrimaryAddress));
    } else {
        vboxWddmAddrSetVram(&pSource->AllocData.Addr,
            pSetVidPnSourceAddress->PrimarySegment,
            pSetVidPnSourceAddress->PrimaryAddress.QuadPart);
    }

    pSource->u8SyncState &= ~VBOXWDDM_HGSYNC_F_SYNCED_LOCATION;

    /*
     * Report the source.
     */
    /* Query the position of the screen to make sure it is up to date. */
    vboxWddmDisplaySettingsQueryPos(pDevExt,
                pSetVidPnSourceAddress->VidPnSourceId,
                &pSource->VScreenPos);

    GaVidPnSourceReport(pDevExt, pSource);

    LOGI("LEAVE, context(0x%p)\n", hAdapter);
    return STATUS_SUCCESS;
}

NTSTATUS
APIENTRY
DxgkDdiGetStandardAllocationDriverData(
    CONST HANDLE hAdapter,
    DXGKARG_GETSTANDARDALLOCATIONDRIVERDATA *pGetStandardAllocationDriverData)
{
    /* should be made pageable. */
    PAGED_CODE();

    LOGI("ENTER==> context(0x%p)\n", hAdapter);

    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_ALLOCINFO pAllocInfo = NULL;

    switch (pGetStandardAllocationDriverData->StandardAllocationType) {
        case D3DKMDT_STANDARDALLOCATION_SHAREDPRIMARYSURFACE:
        {
            LOGI("D3DKMDT_STANDARDALLOCATION_SHAREDPRIMARYSURFACE\n");
            if(pGetStandardAllocationDriverData->pAllocationPrivateDriverData) {
                pAllocInfo = (PVBOXWDDM_ALLOCINFO)pGetStandardAllocationDriverData->pAllocationPrivateDriverData;
                memset(pAllocInfo, 0, sizeof(VBOXWDDM_ALLOCINFO));
                pAllocInfo->enmType = VBOXWDDM_ALLOC_TYPE_STD_SHAREDPRIMARYSURFACE;
                pAllocInfo->SurfDesc.width = pGetStandardAllocationDriverData->pCreateSharedPrimarySurfaceData->Width;
                pAllocInfo->SurfDesc.height = pGetStandardAllocationDriverData->pCreateSharedPrimarySurfaceData->Height;
                pAllocInfo->SurfDesc.format = pGetStandardAllocationDriverData->pCreateSharedPrimarySurfaceData->Format;
                pAllocInfo->SurfDesc.bpp = vboxWddmCalcBitsPerPixel(pAllocInfo->SurfDesc.format);
                pAllocInfo->SurfDesc.pitch = vboxWddmCalcPitch(pGetStandardAllocationDriverData->pCreateSharedPrimarySurfaceData->Width, pAllocInfo->SurfDesc.format);
                pAllocInfo->SurfDesc.cbSize = vboxWddmCalcSize(pAllocInfo->SurfDesc.pitch, pAllocInfo->SurfDesc.height, pAllocInfo->SurfDesc.format);
                pAllocInfo->SurfDesc.depth = 0;
                pAllocInfo->SurfDesc.slicePitch = 0;
                pAllocInfo->SurfDesc.RefreshRate = pGetStandardAllocationDriverData->pCreateSharedPrimarySurfaceData->RefreshRate;
                pAllocInfo->SurfDesc.VidPnSourceId = pGetStandardAllocationDriverData->pCreateSharedPrimarySurfaceData->VidPnSourceId;
            }
            pGetStandardAllocationDriverData->AllocationPrivateDriverDataSize = sizeof(VBOXWDDM_ALLOCINFO);

            pGetStandardAllocationDriverData->ResourcePrivateDriverDataSize = 0;
            break;
        }
        case D3DKMDT_STANDARDALLOCATION_SHADOWSURFACE:
        {
            LOGI("D3DKMDT_STANDARDALLOCATION_SHADOWSURFACE\n");
            UINT bpp = vboxWddmCalcBitsPerPixel(pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Format);
            if (bpp != 0) {
                UINT Pitch = vboxWddmCalcPitch(pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Width, pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Format);
                pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Pitch = Pitch;

                /** @todo need [d/q]word align?? */
                if (pGetStandardAllocationDriverData->pAllocationPrivateDriverData) {
                    pAllocInfo = (PVBOXWDDM_ALLOCINFO)pGetStandardAllocationDriverData->pAllocationPrivateDriverData;
                    pAllocInfo->enmType = VBOXWDDM_ALLOC_TYPE_STD_SHADOWSURFACE;
                    pAllocInfo->SurfDesc.width = pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Width;
                    pAllocInfo->SurfDesc.height = pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Height;
                    pAllocInfo->SurfDesc.format = pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Format;
                    pAllocInfo->SurfDesc.bpp = vboxWddmCalcBitsPerPixel(pAllocInfo->SurfDesc.format);
                    pAllocInfo->SurfDesc.pitch = vboxWddmCalcPitch(pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Width, pAllocInfo->SurfDesc.format);
                    pAllocInfo->SurfDesc.cbSize = vboxWddmCalcSize(pAllocInfo->SurfDesc.pitch, pAllocInfo->SurfDesc.height, pAllocInfo->SurfDesc.format);
                    pAllocInfo->SurfDesc.depth = 0;
                    pAllocInfo->SurfDesc.slicePitch = 0;
                    pAllocInfo->SurfDesc.RefreshRate.Numerator = 0;
                    pAllocInfo->SurfDesc.RefreshRate.Denominator = 1000;
                    pAllocInfo->SurfDesc.VidPnSourceId = D3DDDI_ID_UNINITIALIZED;

                    pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Pitch = pAllocInfo->SurfDesc.pitch;
                }
                pGetStandardAllocationDriverData->AllocationPrivateDriverDataSize = sizeof(VBOXWDDM_ALLOCINFO);

                pGetStandardAllocationDriverData->ResourcePrivateDriverDataSize = 0;
            } else {
                LOGE("Invalid format (%d)\n", pGetStandardAllocationDriverData->pCreateShadowSurfaceData->Format);
                Status = STATUS_INVALID_PARAMETER;
            }
            break;
        }
        case D3DKMDT_STANDARDALLOCATION_STAGINGSURFACE:
        {
            LOGI("D3DKMDT_STANDARDALLOCATION_STAGINGSURFACE\n");
            if(pGetStandardAllocationDriverData->pAllocationPrivateDriverData) {
                pAllocInfo = (PVBOXWDDM_ALLOCINFO)pGetStandardAllocationDriverData->pAllocationPrivateDriverData;
                pAllocInfo->enmType = VBOXWDDM_ALLOC_TYPE_STD_STAGINGSURFACE;
                pAllocInfo->SurfDesc.width = pGetStandardAllocationDriverData->pCreateStagingSurfaceData->Width;
                pAllocInfo->SurfDesc.height = pGetStandardAllocationDriverData->pCreateStagingSurfaceData->Height;
                pAllocInfo->SurfDesc.format = D3DDDIFMT_X8R8G8B8; /* staging has always always D3DDDIFMT_X8R8G8B8 */
                pAllocInfo->SurfDesc.bpp = vboxWddmCalcBitsPerPixel(pAllocInfo->SurfDesc.format);
                pAllocInfo->SurfDesc.pitch = vboxWddmCalcPitch(pGetStandardAllocationDriverData->pCreateStagingSurfaceData->Width, pAllocInfo->SurfDesc.format);
                pAllocInfo->SurfDesc.cbSize = vboxWddmCalcSize(pAllocInfo->SurfDesc.pitch, pAllocInfo->SurfDesc.height, pAllocInfo->SurfDesc.format);
                pAllocInfo->SurfDesc.depth = 0;
                pAllocInfo->SurfDesc.slicePitch = 0;
                pAllocInfo->SurfDesc.RefreshRate.Numerator = 0;
                pAllocInfo->SurfDesc.RefreshRate.Denominator = 1000;
                pAllocInfo->SurfDesc.VidPnSourceId = D3DDDI_ID_UNINITIALIZED;

                pGetStandardAllocationDriverData->pCreateStagingSurfaceData->Pitch = pAllocInfo->SurfDesc.pitch;
            }
            pGetStandardAllocationDriverData->AllocationPrivateDriverDataSize = sizeof(VBOXWDDM_ALLOCINFO);

            pGetStandardAllocationDriverData->ResourcePrivateDriverDataSize = 0;
            break;
        }
        default:
        {
            LOGE("Invalid allocation type (%d)\n",
                 pGetStandardAllocationDriverData->StandardAllocationType);
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
    }

    LOGI("LEAVE: status(0x%x), context(0x%p)\n", Status, hAdapter);

    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiDescribeAllocation(
    CONST HANDLE hAdapter,
    DXGKARG_DESCRIBEALLOCATION *pDescribeAllocation)
{
    LOGI("ENTER==> hAdapter(0x%p)\n", hAdapter);

    PVBOXWDDM_ALLOCATION pAllocation = (PVBOXWDDM_ALLOCATION)pDescribeAllocation->hAllocation;

#ifdef VBOX_WITH_VMSVGA3D_DX
    /* Check if this is a request from the D3D driver. */
    if (pAllocation->enmType == VBOXWDDM_ALLOC_TYPE_D3D)
        return DxgkDdiDXDescribeAllocation(hAdapter, pDescribeAllocation);
#endif

    pDescribeAllocation->Width = pAllocation->AllocData.SurfDesc.width;
    pDescribeAllocation->Height = pAllocation->AllocData.SurfDesc.height;
    pDescribeAllocation->Format = pAllocation->AllocData.SurfDesc.format;
    memset(&pDescribeAllocation->MultisampleMethod, 0,
           sizeof(pDescribeAllocation->MultisampleMethod));
    pDescribeAllocation->RefreshRate.Numerator = g_RefreshRate * 1000;
    pDescribeAllocation->RefreshRate.Denominator = 1000;
    pDescribeAllocation->PrivateDriverFormatAttribute = 0;

    LOGI("LEAVE<== hAdapter(0x%p)\n", hAdapter);

    return STATUS_SUCCESS;
}

DECLINLINE(PVBOXWDDM_ALLOCATION) vboxWddmGetAllocationFromHandle(
    PVBOXMP_DEVEXT pDevExt, D3DKMT_HANDLE hAllocation)
{
    DXGKARGCB_GETHANDLEDATA GhData;
    GhData.hObject = hAllocation;
    GhData.Type = DXGK_HANDLE_ALLOCATION;
    GhData.Flags.Value = 0;
    return (PVBOXWDDM_ALLOCATION)pDevExt->u.primary.DxgkInterface.DxgkCbGetHandleData(&GhData);
}

DECLINLINE(PVBOXWDDM_OPENALLOCATION) VBoxWddmOaSearchLocked(
    PVBOXWDDM_DEVICE pDevice, PVBOXWDDM_ALLOCATION pAllocation)
{
    for (PLIST_ENTRY pCur = pAllocation->OpenList.Flink; pCur != &pAllocation->OpenList;
         pCur = pCur->Flink) {
        PVBOXWDDM_OPENALLOCATION pCurOa = CONTAINING_RECORD(pCur, VBOXWDDM_OPENALLOCATION, ListEntry);
        if (pCurOa->pDevice == pDevice) {
            return pCurOa;
        }
    }
    return NULL;
}

DECLINLINE(void) VBoxWddmOaHostIDReleaseLocked(PVBOXWDDM_OPENALLOCATION pOa)
{
    PVBOXWDDM_ALLOCATION pAllocation = pOa->pAllocation;
    --pOa->cHostIDRefs;
    --pAllocation->AllocData.cHostIDRefs;
    if (!pAllocation->AllocData.cHostIDRefs) {
        pAllocation->AllocData.hostID = 0;
    }
}

DECLINLINE(void) VBoxWddmOaHostIDCheckReleaseLocked(PVBOXWDDM_OPENALLOCATION pOa)
{
    if (pOa->cHostIDRefs) {
        VBoxWddmOaHostIDReleaseLocked(pOa);
    }
}

DECLINLINE(void) VBoxWddmOaRelease(PVBOXWDDM_OPENALLOCATION pOa)
{
    PVBOXWDDM_ALLOCATION pAllocation = pOa->pAllocation;
    KIRQL OldIrql;
    KeAcquireSpinLock(&pAllocation->OpenLock, &OldIrql);
    VBoxWddmOaHostIDCheckReleaseLocked(pOa);
    --pAllocation->cOpens;
    UINT32 cOpens = --pOa->cOpens;
    if (!cOpens) {
        RemoveEntryList(&pOa->ListEntry);
        KeReleaseSpinLock(&pAllocation->OpenLock, OldIrql);
        vboxWddmMemFree(pOa);
    } else {
        KeReleaseSpinLock(&pAllocation->OpenLock, OldIrql);
    }
}

NTSTATUS
APIENTRY
DxgkDdiOpenAllocation(
    CONST HANDLE hDevice,
    CONST DXGKARG_OPENALLOCATION *pOpenAllocation)
{
    /* DxgkDdiOpenAllocation should be made pageable. */
    PAGED_CODE();

    LOGI("ENTER==> hDevice(0x%p)\n", hDevice);

    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_DEVICE pDevice = (PVBOXWDDM_DEVICE)hDevice;
    PVBOXMP_DEVEXT pDevExt = pDevice->pAdapter;
    PVBOXWDDM_RCINFO pRcInfo = NULL;
    if (pOpenAllocation->PrivateDriverSize) {
        if (pOpenAllocation->PrivateDriverSize == sizeof(VBOXWDDM_RCINFO)) {
            pRcInfo = (PVBOXWDDM_RCINFO)pOpenAllocation->pPrivateDriverData;
        } else {
            LOGF("Invalid PrivateDriverSize %d\n", pOpenAllocation->PrivateDriverSize);
            Status = STATUS_INVALID_PARAMETER;
            return Status;
        }
    }

    UINT i = 0;
    for (; i < pOpenAllocation->NumAllocations; ++i) {
        DXGK_OPENALLOCATIONINFO *pInfo = &pOpenAllocation->pOpenAllocation[i];
#ifdef VBOX_WITH_VMSVGA3D_DX
        Assert(   pInfo->PrivateDriverDataSize == sizeof(VBOXDXALLOCATIONDESC)
               || pInfo->PrivateDriverDataSize == sizeof(VBOXWDDM_ALLOCINFO));
#else
        Assert(pInfo->PrivateDriverDataSize == sizeof (VBOXWDDM_ALLOCINFO));
#endif
        Assert(pInfo->pPrivateDriverData);
        PVBOXWDDM_ALLOCATION pAllocation = vboxWddmGetAllocationFromHandle(pDevExt, pInfo->hAllocation);
        if (!pAllocation) {
            LOGF("invalid handle\n");
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        if (pRcInfo) {
            if (pInfo->PrivateDriverDataSize != sizeof(VBOXWDDM_ALLOCINFO) ||
                !pInfo->pPrivateDriverData) {
                LOGE("invalid data size\n");
                Status = STATUS_INVALID_PARAMETER;
                break;
            }
        }

        KIRQL OldIrql;
        PVBOXWDDM_OPENALLOCATION pOa;
        KeAcquireSpinLock(&pAllocation->OpenLock, &OldIrql);
        pOa = VBoxWddmOaSearchLocked(pDevice, pAllocation);
        if (pOa) {
            ++pOa->cOpens;
            ++pAllocation->cOpens;
            KeReleaseSpinLock(&pAllocation->OpenLock, OldIrql);
        } else {
            KeReleaseSpinLock(&pAllocation->OpenLock, OldIrql);
            pOa = (PVBOXWDDM_OPENALLOCATION)vboxWddmMemAllocZero(sizeof(VBOXWDDM_OPENALLOCATION));
            if (!pOa) {
                LOGF("failed to allocation alloc info\n");
                Status = STATUS_INSUFFICIENT_RESOURCES;
                break;
            }
            pOa->hAllocation = pInfo->hAllocation;
            pOa->pAllocation = pAllocation;
            pOa->pDevice = pDevice;
            pOa->cOpens = 1;

            PVBOXWDDM_OPENALLOCATION pConcurrentOa;
            KeAcquireSpinLock(&pAllocation->OpenLock, &OldIrql);
            pConcurrentOa = VBoxWddmOaSearchLocked(pDevice, pAllocation);
            if (!pConcurrentOa) {
                InsertHeadList(&pAllocation->OpenList, &pOa->ListEntry);
            } else {
                ++pConcurrentOa->cOpens;
            }
            ++pAllocation->cOpens;
            KeReleaseSpinLock(&pAllocation->OpenLock, OldIrql);
            if (pConcurrentOa) {
                vboxWddmMemFree(pOa);
                pOa = pConcurrentOa;
            }
        }

        pInfo->hDeviceSpecificAllocation = pOa;
    }

    if (Status != STATUS_SUCCESS) {
        for (UINT j = 0; j < i; ++j) {
            DXGK_OPENALLOCATIONINFO *pInfo2Free = &pOpenAllocation->pOpenAllocation[j];
            PVBOXWDDM_OPENALLOCATION pOa2Free = (PVBOXWDDM_OPENALLOCATION)pInfo2Free->hDeviceSpecificAllocation;
            VBoxWddmOaRelease(pOa2Free);
        }
    }

    LOGI("LEAVE, hDevice(0x%p), Status 0x%x\n", hDevice, Status);

    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiCloseAllocation(
    CONST HANDLE hDevice,
    CONST DXGKARG_CLOSEALLOCATION *pCloseAllocation)
{
    /* DxgkDdiCloseAllocation should be made pageable. */
    PAGED_CODE();

    LOGI("ENTER, hDevice(0x%p)\n", hDevice);

    for (UINT i = 0; i < pCloseAllocation->NumAllocations; ++i)
    {
        PVBOXWDDM_OPENALLOCATION pOa2Free = (PVBOXWDDM_OPENALLOCATION)pCloseAllocation->pOpenHandleList[i];
        PVBOXWDDM_ALLOCATION pAllocation = pOa2Free->pAllocation;
        if (pAllocation->cShRcRefs < pOa2Free->cShRcRefs) {
            LOGE("invalid cShRcRefs: %u < %u \n", pAllocation->cShRcRefs, pOa2Free->cShRcRefs);
        }
        pAllocation->cShRcRefs -= pOa2Free->cShRcRefs;
        VBoxWddmOaRelease(pOa2Free);
    }

    LOGI("LEAVE, hDevice(0x%p)\n", hDevice);

    return STATUS_SUCCESS;
}

DECLINLINE(VOID) vboxWddmResourceRetain(PVBOXWDDM_RESOURCE pResource)
{
    ASMAtomicIncU32(&pResource->cRefs);
}

PVBOXWDDM_ALLOCATION vboxWddmAllocationCreateFromResource(
    PVBOXWDDM_RESOURCE pResource, UINT32 iIndex)
{
    PVBOXWDDM_ALLOCATION pAllocation = NULL;
    if (pResource) {
        if (iIndex < pResource->cAllocations) {
            pAllocation = &pResource->aAllocations[iIndex];
            memset(pAllocation, 0, sizeof(VBOXWDDM_ALLOCATION));
        }
        vboxWddmResourceRetain(pResource);
    } else {
        pAllocation = (PVBOXWDDM_ALLOCATION)vboxWddmMemAllocZero(
                                    sizeof(VBOXWDDM_ALLOCATION));
        if (!pAllocation) {
            LOGE("Failed to memory alloc for pAllocation\n");
        }
    }

    if (pAllocation) {
        if (pResource) {
            pAllocation->pResource = pResource;
            pAllocation->iIndex = iIndex;
        }
    }

    return pAllocation;
}

DECLINLINE(VOID) vboxWddmResourceDestroy(PVBOXWDDM_RESOURCE pResource)
{
    vboxWddmMemFree(pResource);
}

VOID vboxWddmResourceRelease(PVBOXWDDM_RESOURCE pResource)
{
    UINT32 cRefs = ASMAtomicDecU32(&pResource->cRefs);
    if (!cRefs) {
        vboxWddmResourceDestroy(pResource);
    }
}

void vboxWddmAllocationDeleteFromResource(PVBOXWDDM_RESOURCE pResource,
                                          PVBOXWDDM_ALLOCATION pAllocation)
{
    if (pResource) {
        vboxWddmResourceRelease(pResource);
    } else {
        vboxWddmMemFree(pAllocation);
    }
}

NTSTATUS vboxWddmAllocationCreate(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_RESOURCE pResource,
                                  UINT32 iIndex, DXGK_ALLOCATIONINFO* pAllocationInfo)
{
    PAGED_CODE();

    RT_NOREF(pDevExt);

    NTSTATUS Status = STATUS_SUCCESS;

    if (pAllocationInfo->PrivateDriverDataSize < sizeof(VBOXWDDM_ALLOCINFO)) {
        LOGE("ERROR: PrivateDriverDataSize(%d) less than header size(%d)\n",
             pAllocationInfo->PrivateDriverDataSize, sizeof(VBOXWDDM_ALLOCINFO));
        Status = STATUS_INVALID_PARAMETER;
        return Status;
    }

    PVBOXWDDM_ALLOCINFO pAllocInfo = (PVBOXWDDM_ALLOCINFO)pAllocationInfo->pPrivateDriverData;
    PVBOXWDDM_ALLOCATION pAllocation = vboxWddmAllocationCreateFromResource(pResource, iIndex);
    if (pAllocation) {
        pAllocationInfo->pPrivateDriverData = NULL;
        pAllocationInfo->PrivateDriverDataSize = 0;
        pAllocationInfo->Alignment = 0;
        pAllocationInfo->PitchAlignedSize = 0;
        pAllocationInfo->HintedBank.Value = 0;
        pAllocationInfo->PreferredSegment.Value = 0;
        pAllocationInfo->SupportedReadSegmentSet = 1;
        pAllocationInfo->SupportedWriteSegmentSet = 1;
        pAllocationInfo->EvictionSegmentSet = 0;
        pAllocationInfo->MaximumRenamingListLength = 0;
        pAllocationInfo->hAllocation = pAllocation;
        pAllocationInfo->Flags.Value = 0;
        pAllocationInfo->pAllocationUsageHint = NULL;
        pAllocationInfo->AllocationPriority = D3DDDI_ALLOCATIONPRIORITY_NORMAL;

        pAllocation->enmType = pAllocInfo->enmType;
        pAllocation->AllocData.Addr.SegmentId = 0;
        pAllocation->AllocData.Addr.offVram = VBOXVIDEOOFFSET_VOID;
        pAllocation->bVisible = FALSE;
        pAllocation->bAssigned = FALSE;
        KeInitializeSpinLock(&pAllocation->OpenLock);
        InitializeListHead(&pAllocation->OpenList);
        pAllocation->CurVidPnSourceId = -1;

        switch (pAllocInfo->enmType) {
            case VBOXWDDM_ALLOC_TYPE_STD_SHAREDPRIMARYSURFACE:
            case VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC:
            case VBOXWDDM_ALLOC_TYPE_STD_SHADOWSURFACE:
            case VBOXWDDM_ALLOC_TYPE_STD_STAGINGSURFACE:
            {
                pAllocation->fRcFlags = pAllocInfo->fFlags;
                pAllocation->AllocData.SurfDesc = pAllocInfo->SurfDesc;
                pAllocation->AllocData.hostID = pAllocInfo->hostID;

                pAllocationInfo->Size = pAllocInfo->SurfDesc.cbSize;

                switch (pAllocInfo->enmType) {
                    case VBOXWDDM_ALLOC_TYPE_STD_SHAREDPRIMARYSURFACE:
                        if (SvgaIsDXSupported(pDevExt))
                        {
                            pAllocationInfo->PreferredSegment.Value      = 0;
                            pAllocationInfo->SupportedReadSegmentSet     = 1; /* VRAM */
                            pAllocationInfo->SupportedWriteSegmentSet    = 1; /* VRAM */
                            /// @todo Required?  pAllocationInfo->Flags.CpuVisible = 1;
                        }
                        break;
                    case VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC:
                    {
                        /*
                         * Mark the allocation as visible to the CPU so we can
                         * lock it in the user mode driver for SYSTEM pool allocations.
                         * See @bugref{8040} for further information.
                         */
                        if (!pAllocInfo->fFlags.SharedResource && !pAllocInfo->hostID) {
                            pAllocationInfo->Flags.CpuVisible = 1;
                        }

                        if (pAllocInfo->fFlags.SharedResource) {
                            pAllocation->hSharedHandle = (HANDLE)pAllocInfo->hSharedHandle;
                        }
                        break;
                    }
                    case VBOXWDDM_ALLOC_TYPE_STD_SHADOWSURFACE:
                    case VBOXWDDM_ALLOC_TYPE_STD_STAGINGSURFACE:
                        if (SvgaIsDXSupported(pDevExt))
                        {
                            pAllocationInfo->PreferredSegment.Value      = 0;
                            pAllocationInfo->SupportedReadSegmentSet     = 1; /* VRAM */
                            pAllocationInfo->SupportedWriteSegmentSet    = 1; /* VRAM */
                        }
                        pAllocationInfo->Flags.CpuVisible = 1;
                        break;
                    default:
                    {
                        LOGE("invalid pAllocInfo->enmType \n");
                    }
                }

                if (Status == STATUS_SUCCESS) {
                    pAllocation->UsageHint.Version = 0;
                    pAllocation->UsageHint.v1.Flags.Value = 0;
                    pAllocation->UsageHint.v1.Format = pAllocInfo->SurfDesc.format;
                    pAllocation->UsageHint.v1.SwizzledFormat = 0;
                    pAllocation->UsageHint.v1.ByteOffset = 0;
                    pAllocation->UsageHint.v1.Width = pAllocation->AllocData.SurfDesc.width;
                    pAllocation->UsageHint.v1.Height = pAllocation->AllocData.SurfDesc.height;
                    pAllocation->UsageHint.v1.Pitch = pAllocation->AllocData.SurfDesc.pitch;
                    pAllocation->UsageHint.v1.Depth = 0;
                    pAllocation->UsageHint.v1.SlicePitch = 0;

                    pAllocationInfo->pAllocationUsageHint = &pAllocation->UsageHint;
                }

                break;
            }
            case VBOXWDDM_ALLOC_TYPE_UMD_HGSMI_BUFFER:
            {
                LOGE("UMD HGSMI BUFFER is not supported now\n");
                Status = STATUS_INVALID_PARAMETER;
                break;
            }
            default:
            {
                LOGE("ERROR: invalid alloc info type(%d)\n", pAllocInfo->enmType);
                Status = STATUS_INVALID_PARAMETER;
                break;
            }
        }

        if (Status != STATUS_SUCCESS) {
            vboxWddmAllocationDeleteFromResource(pResource, pAllocation);
        }
    } else {
        LOGE("ERROR: failed to create allocation description\n");
        Status = STATUS_NO_MEMORY;
    }

    return Status;
}

VOID vboxWddmAllocationCleanup(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_ALLOCATION pAllocation)
{
    /* do nothing */
    RT_NOREF(pDevExt);

    switch (pAllocation->enmType)
    {
        case VBOXWDDM_ALLOC_TYPE_STD_SHAREDPRIMARYSURFACE:
        case VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC:
        {
            break;
        }
        case VBOXWDDM_ALLOC_TYPE_UMD_HGSMI_BUFFER:
        {
            break;
        }
        default:
            break;
    }
}

DECLINLINE(VOID) vboxWddmAllocationDestroy(PVBOXWDDM_ALLOCATION pAllocation)
{
    vboxWddmAllocationDeleteFromResource(pAllocation->pResource, pAllocation);
}

NTSTATUS APIENTRY DxgkDdiCreateAllocation(
    CONST HANDLE hAdapter,
    DXGKARG_CREATEALLOCATION *pCreateAllocation)
{
    /* DxgkDdiCreateAllocation should be made pageable. */
    PAGED_CODE();

    LOGI("ENTER==> context(0x%p)\n", hAdapter);

#ifdef VBOX_WITH_VMSVGA3D_DX
    /* The driver distinguished between the legacy and the new D3D(DX) requests by checking the size. */

    /* Check if this is a request from the new D3D driver. */
    if (   pCreateAllocation->PrivateDriverDataSize == 0
        && pCreateAllocation->NumAllocations == 1
        && pCreateAllocation->pAllocationInfo[0].PrivateDriverDataSize == sizeof(VBOXDXALLOCATIONDESC))
        return DxgkDdiDXCreateAllocation(hAdapter, pCreateAllocation);
#endif

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_RESOURCE pResource = NULL;

    if (pCreateAllocation->PrivateDriverDataSize) {
        if (pCreateAllocation->PrivateDriverDataSize < sizeof(VBOXWDDM_RCINFO)) {
            LOGE("invalid private data size (%d)\n",
                 pCreateAllocation->PrivateDriverDataSize);
            return STATUS_INVALID_PARAMETER;
        }

        PVBOXWDDM_RCINFO pRcInfo = (PVBOXWDDM_RCINFO)pCreateAllocation->pPrivateDriverData;
        if (pRcInfo->cAllocInfos != pCreateAllocation->NumAllocations) {
            LOGE("invalid number of allocations passed in, (%u), expected (%u)\n",
                 pRcInfo->cAllocInfos, pCreateAllocation->NumAllocations);
            return STATUS_INVALID_PARAMETER;
        }

        /* a check to ensure we do not get the allocation size which is too big to overflow the 32bit value */
        if (VBOXWDDM_TRAILARRAY_MAXELEMENTSU32(VBOXWDDM_RESOURCE, aAllocations) <
            pRcInfo->cAllocInfos) {
            LOGE("number of allocations passed too big (%d), max is (%d)\n",
                 pRcInfo->cAllocInfos,
                 VBOXWDDM_TRAILARRAY_MAXELEMENTSU32(VBOXWDDM_RESOURCE, aAllocations));
            return STATUS_INVALID_PARAMETER;
        }

        pResource = (PVBOXWDDM_RESOURCE)vboxWddmMemAllocZero(
            RT_UOFFSETOF_DYN(VBOXWDDM_RESOURCE, aAllocations[pRcInfo->cAllocInfos]));
        if (!pResource) {
            LOGE("vboxWddmMemAllocZero failed for (%d) allocations\n",
                 pRcInfo->cAllocInfos);
            return STATUS_NO_MEMORY;
        }

        pResource->cRefs = 1;
        pResource->cAllocations = pRcInfo->cAllocInfos;
        pResource->fFlags = pRcInfo->fFlags;
        pResource->RcDesc = pRcInfo->RcDesc;
    }

    for (UINT i = 0; i < pCreateAllocation->NumAllocations; ++i) {
        Status = vboxWddmAllocationCreate(pDevExt, pResource, i,
                        &pCreateAllocation->pAllocationInfo[i]);
        if (Status != STATUS_SUCCESS) {
            LOGE("vboxWddmAllocationCreate(%d) failed, Status(0x%x)\n", i, Status);
            /* note: i-th allocation is expected to be cleared in a fail handling code above */
            for (UINT j = 0; j < i; ++j) {
                PVBOXWDDM_ALLOCATION pAllocation = (PVBOXWDDM_ALLOCATION)
                        pCreateAllocation->pAllocationInfo[j].hAllocation;
                vboxWddmAllocationCleanup(pDevExt, pAllocation);
                vboxWddmAllocationDestroy(pAllocation);
            }
            break;
        }
    }

    if (Status == STATUS_SUCCESS) {
        pCreateAllocation->hResource = pResource;
    } else {
        if (pResource) {
            vboxWddmResourceRelease(pResource);
        }
    }

    LOGI("LEAVE status(0x%x), context(0x%p)\n", Status, hAdapter);
    return Status;
}

VOID vboxWddmAllocationCleanupAssignment(PVBOXMP_DEVEXT pDevExt,
                                         PVBOXWDDM_ALLOCATION pAllocation)
{
    switch (pAllocation->enmType) {
        case VBOXWDDM_ALLOC_TYPE_STD_SHAREDPRIMARYSURFACE:
        case VBOXWDDM_ALLOC_TYPE_UMD_RC_GENERIC:
        {
            if (pAllocation->bAssigned) {
                /** @todo do we need to notify host? */
                vboxWddmAssignPrimary(
                    &pDevExt->aSources[pAllocation->AllocData.SurfDesc.VidPnSourceId],
                    NULL, pAllocation->AllocData.SurfDesc.VidPnSourceId);
            }
            break;
        }
        default:
            break;
    }
}

VOID vboxWddmResourceWaitDereference(PVBOXWDDM_RESOURCE pResource)
{
    vboxWddmCounterU32Wait(&pResource->cRefs, 1);
}

NTSTATUS
APIENTRY
DxgkDdiDestroyAllocation(
    CONST HANDLE hAdapter,
    CONST DXGKARG_DESTROYALLOCATION *pDestroyAllocation)
{
    /* DxgkDdiDestroyAllocation should be made pageable. */
    PAGED_CODE();

    LOGI("ENTER==> context(0x%p)\n", hAdapter);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    for (UINT i = 0; i < pDestroyAllocation->NumAllocations; ++i)
    {
        PVBOXWDDM_ALLOCATION pAllocation = (PVBOXWDDM_ALLOCATION)pDestroyAllocation->pAllocationList[0];
        if (pAllocation->CurVidPnSourceId != -1)
        {
            VBOXWDDM_SOURCE *pSource = &pDevExt->aSources[pAllocation->CurVidPnSourceId];
            vboxWddmAssignPrimary(pSource, NULL, pAllocation->CurVidPnSourceId);
        }
    }

#ifdef VBOX_WITH_VMSVGA3D_DX
    /* Check if this is a request from the D3D driver. */
    if (pDestroyAllocation->NumAllocations >= 1)
    {
        PVBOXWDDM_ALLOCATION pAllocation = (PVBOXWDDM_ALLOCATION)pDestroyAllocation->pAllocationList[0];
        if (pAllocation->enmType == VBOXWDDM_ALLOC_TYPE_D3D)
            return DxgkDdiDXDestroyAllocation(hAdapter, pDestroyAllocation);
    }
#endif

    NTSTATUS Status = STATUS_SUCCESS;

    PVBOXWDDM_RESOURCE pRc = (PVBOXWDDM_RESOURCE)pDestroyAllocation->hResource;

    if (pRc) {
        if (pRc->cAllocations != pDestroyAllocation->NumAllocations) {
            LOGE("invalid NumAllocatins: expect %u, get %u\n",
                 pRc->cAllocations, pDestroyAllocation->NumAllocations);
            return STATUS_INVALID_PARAMETER;
        }
    }

    for (UINT i = 0; i < pDestroyAllocation->NumAllocations; ++i) {
        PVBOXWDDM_ALLOCATION pAlloc = (PVBOXWDDM_ALLOCATION)
                            pDestroyAllocation->pAllocationList[i];
        vboxWddmAllocationCleanupAssignment(pDevExt, pAlloc);
        /* wait for all current allocation-related ops are completed */
        vboxWddmAllocationCleanup(pDevExt, pAlloc);
        vboxWddmAllocationDestroy(pAlloc);
    }

    if (pRc) {
        /* wait for all current resource-related ops are completed */
        vboxWddmResourceWaitDereference(pRc);
        vboxWddmResourceRelease(pRc);
    }

    LOGI("LEAVE ==> status(0x%x), context(0x%p)\n", Status, hAdapter);
    return Status;
}

NTSTATUS
APIENTRY
DxgkDdiCollectDbgInfo(
    CONST HANDLE  hAdapter,
    CONST DXGKARG_COLLECTDBGINFO* pCollectDbgInfo
)
{
    RT_NOREF(hAdapter, pCollectDbgInfo);

    LOGE("DxgkDdiCollectDbgInfo\n");
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;
    VBOXWDDM_EXT_GA *pGaDevExt = pDevExt->pGa;
    LOGI("Collect Reason is (0x%p), LastSubmittedFenceId is %d, LastCompletedFenceId is %d,"
        "PreemptionFenceId is %d, LastCompletedSeqNo is %d\n", pCollectDbgInfo->Reason,
        pGaDevExt->u32LastSubmittedFenceId, pGaDevExt->u32LastCompletedFenceId,
        pGaDevExt->u32PreemptionFenceId, pGaDevExt->u32LastCompletedSeqNo);
    return STATUS_SUCCESS;
}

static NTSTATUS DxgkDdiStopDeviceAndReleasePostDisplayOwnership(
    _In_   PVOID MiniportDeviceContext,
    _In_   D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _Out_  PDXGK_DISPLAY_INFORMATION DisplayInfo
)
{
    LOGE("MiniportDeviceContext\n");

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    if (TargetId >= (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays)
        return STATUS_NOT_SUPPORTED;

    DisplayInfo->Width = pDevExt->aSources[TargetId].AllocData.SurfDesc.width;
    DisplayInfo->Height = pDevExt->aSources[TargetId].AllocData.SurfDesc.height;
    DisplayInfo->Pitch = pDevExt->aSources[TargetId].AllocData.SurfDesc.pitch;
    DisplayInfo->ColorFormat = pDevExt->aSources[TargetId].AllocData.SurfDesc.format;
    DisplayInfo->TargetId = TargetId;
    DisplayInfo->PhysicAddress = VBoxCommonFromDeviceExt(pDevExt)->phVRAM;
    DisplayInfo->AcpiId = 0;

    LOGI("width %u height %u pitch %u format %d\n",
         DisplayInfo->Width, DisplayInfo->Height, DisplayInfo->Pitch, DisplayInfo->ColorFormat);

    if (pDevExt->pvVisibleVram != NULL) {
        memset(pDevExt->pvVisibleVram, 0,  DisplayInfo->Height * DisplayInfo->Pitch);
    }

    SVGARegWrite(pDevExt->pGa->hw.pSvga, SVGA_REG_CONFIG_DONE, SVGA_CONFIG_DONE_FB_MODE);

    DxgkDdiStopDevice(MiniportDeviceContext);
    return STATUS_SUCCESS;
}

static NTSTATUS DxgkDdiSystemDisplayEnable(
    _In_   PVOID MiniportDeviceContext,
    _In_   D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _In_   PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags,
    _Out_  UINT* Width,
    _Out_  UINT* Height,
    _Out_  D3DDDIFORMAT* ColorFormat
)
{
    RT_NOREF(Flags);

    LOGE("SystemDisplayEnable\n");

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;

    if (TargetId >= (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays)
        return STATUS_NOT_SUPPORTED;

    pDevExt->SysDisplaySourceId = TargetId;
    *Width = pDevExt->aSources[TargetId].AllocData.SurfDesc.width;
    *Height = pDevExt->aSources[TargetId].AllocData.SurfDesc.height;
    *ColorFormat = pDevExt->aSources[TargetId].AllocData.SurfDesc.format;

    // Do not aquire lock in bsod process, write register directly
    SVGARegisterIndexWrite(pDevExt->pGa->hw.pSvga, SVGA_REG_CONFIG_DONE);
    SVGAPortWrite(pDevExt->pGa->hw.pSvga, SVGA_VALUE_PORT, SVGA_CONFIG_DONE_FB_MODE);

    return STATUS_SUCCESS;
}

static VOID DxgkDdiSystemDisplayWrite(
    _In_  PVOID MiniportDeviceContext,
    _In_  PVOID Source,
    _In_  UINT SourceWidth,
    _In_  UINT SourceHeight,
    _In_  UINT SourceStride,
    _In_  UINT PositionX,
    _In_  UINT PositionY
)
{
    RT_NOREF(SourceStride);

    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)MiniportDeviceContext;
    DWORD BytesPP = vboxWddmCalcBitsPerPixel(pDevExt->aSources[pDevExt->SysDisplaySourceId].AllocData.SurfDesc.format) / 8;
    for (DWORD i = 0; i < SourceHeight; i++) {
        memcpy(pDevExt->pvVisibleVram + (PositionX + pDevExt->aSources[pDevExt->SysDisplaySourceId].AllocData.SurfDesc.width * (i + PositionY)) * BytesPP,
            (UINT8*)Source + i * SourceWidth * BytesPP, SourceWidth * BytesPP);
    }
}

static NTSTATUS APIENTRY DxgkDdiPresentDisplayOnly(
    _In_ const HANDLE hAdapter,
    _In_ const DXGKARG_PRESENT_DISPLAYONLY *pPresentDisplayOnly)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    LOGI("ENTER, hAdapter(0x%p)\n", hAdapter);

    if (pDevExt->enmHwType == VBOXVIDEO_HWTYPE_VMSVGA)
    {
        Status = GaDxgkDdiPresentDisplayOnly(hAdapter, pPresentDisplayOnly);
    } else {
        LOGE("Just support vmsvga\n");
        Status = STATUS_NOT_SUPPORTED;
    }

    LOGI("LEAVE, hAdapter(0x%p), Status=%d", hAdapter, Status);

    return STATUS_SUCCESS;
}

extern "C"
NTSTATUS vboxWddmInitDisplayOnlyDriver(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    KMDDOD_INITIALIZATION_DATA DriverInitializationData = {0};

    DriverInitializationData.Version = DXGKDDI_INTERFACE_VERSION_WIN8;

    DriverInitializationData.DxgkDdiAddDevice = DxgkDdiAddDevice;
    DriverInitializationData.DxgkDdiStartDevice = DxgkDdiStartDevice;
    DriverInitializationData.DxgkDdiStopDevice = DxgkDdiStopDevice;
    DriverInitializationData.DxgkDdiRemoveDevice = DxgkDdiRemoveDevice;
    DriverInitializationData.DxgkDdiDispatchIoRequest = DxgkDdiDispatchIoRequest;
    DriverInitializationData.DxgkDdiInterruptRoutine = DxgkDdiInterruptRoutine;
    DriverInitializationData.DxgkDdiDpcRoutine = DxgkDdiDpcRoutine;
    DriverInitializationData.DxgkDdiQueryChildRelations = DxgkDdiQueryChildRelations;
    DriverInitializationData.DxgkDdiQueryChildStatus = DxgkDdiQueryChildStatus;
    DriverInitializationData.DxgkDdiQueryDeviceDescriptor = DxgkDdiQueryDeviceDescriptor;
    DriverInitializationData.DxgkDdiSetPowerState = DxgkDdiSetPowerState;
    DriverInitializationData.DxgkDdiNotifyAcpiEvent = DxgkDdiNotifyAcpiEvent;
    DriverInitializationData.DxgkDdiResetDevice = DxgkDdiResetDevice;
    DriverInitializationData.DxgkDdiUnload = DxgkDdiUnload;
    DriverInitializationData.DxgkDdiQueryInterface = DxgkDdiQueryInterface;
    DriverInitializationData.DxgkDdiControlEtwLogging = DxgkDdiControlEtwLogging;
    DriverInitializationData.DxgkDdiQueryAdapterInfo = DxgkDdiQueryAdapterInfo;
    DriverInitializationData.DxgkDdiSetPalette = DxgkDdiSetPalette;
    DriverInitializationData.DxgkDdiSetPointerPosition = DxgkDdiSetPointerPosition;
    DriverInitializationData.DxgkDdiSetPointerShape = DxgkDdiSetPointerShape;
    DriverInitializationData.DxgkDdiEscape = DxgkDdiEscape;
    DriverInitializationData.DxgkDdiCollectDbgInfo = DxgkDdiCollectDbgInfo;
    DriverInitializationData.DxgkDdiIsSupportedVidPn = DxgkDdiIsSupportedVidPn;
    DriverInitializationData.DxgkDdiRecommendFunctionalVidPn = DxgkDdiRecommendFunctionalVidPn;
    DriverInitializationData.DxgkDdiEnumVidPnCofuncModality = DxgkDdiEnumVidPnCofuncModality;
    DriverInitializationData.DxgkDdiSetVidPnSourceVisibility = DxgkDdiSetVidPnSourceVisibility;
    DriverInitializationData.DxgkDdiCommitVidPn = DxgkDdiCommitVidPn;
    DriverInitializationData.DxgkDdiUpdateActiveVidPnPresentPath = DxgkDdiUpdateActiveVidPnPresentPath;
    DriverInitializationData.DxgkDdiRecommendMonitorModes = DxgkDdiRecommendMonitorModes;
    DriverInitializationData.DxgkDdiQueryVidPnHWCapability = DxgkDdiQueryVidPnHWCapability;
    DriverInitializationData.DxgkDdiPresentDisplayOnly = DxgkDdiPresentDisplayOnly;
    DriverInitializationData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = DxgkDdiStopDeviceAndReleasePostDisplayOwnership;
    DriverInitializationData.DxgkDdiSystemDisplayEnable = DxgkDdiSystemDisplayEnable;
    DriverInitializationData.DxgkDdiSystemDisplayWrite = DxgkDdiSystemDisplayWrite;

    NTSTATUS Status = DxgkInitializeDisplayOnlyDriver(pDriverObject, pRegistryPath, &DriverInitializationData);
    if (!NT_SUCCESS(Status))
    {
        LOGE("DxgkInitializeDisplayOnlyDriver failed! Status 0x%x\n", Status);
    }
    return Status;
}

NTSTATUS APIENTRY DxgkDdiAcquireSwizzlingRange(
    CONST HANDLE hAdapter,
    DXGKARG_ACQUIRESWIZZLINGRANGE *pAcquireSwizzlingRange)
{
    RT_NOREF(hAdapter, pAcquireSwizzlingRange);
    VBoxGpuDbgBreak();

    LOGI("==>, hAdapter(0x%p)\n", hAdapter);

    return STATUS_SUCCESS;
}

NTSTATUS APIENTRY DxgkDdiReleaseSwizzlingRange(
    CONST HANDLE hAdapter,
    CONST DXGKARG_RELEASESWIZZLINGRANGE *pReleaseSwizzlingRange)
{
    RT_NOREF(hAdapter, pReleaseSwizzlingRange);
    VBoxGpuDbgBreak();
    LOGI("==>, hAdapter(0x%p)\n", hAdapter);

    return STATUS_SUCCESS;
}

NTSTATUS APIENTRY CALLBACK DxgkDdiResetFromTimeout(
    CONST HANDLE hAdapter)
{
    RT_NOREF(hAdapter);
    VBoxGpuDbgBreak();
    LOGI("==>, hAdapter(0x%p)\n", hAdapter);
    LOGF("try to reset from timeout\n");
    return STATUS_SUCCESS;
}

NTSTATUS APIENTRY CALLBACK DxgkDdiRestartFromTimeout(
    CONST HANDLE hAdapter)
{
    RT_NOREF(hAdapter);
    VBoxGpuDbgBreak();
    LOGI("ENTER==>, hAdapter(0x%p)\n", hAdapter);

    return STATUS_SUCCESS;
}

NTSTATUS APIENTRY DxgkDdiControlInterrupt(
    CONST HANDLE hAdapter,
    CONST DXGK_INTERRUPT_TYPE InterruptType,
    BOOLEAN Enable)
{
    LOGI("==> hAdapter(0x%p)\n", hAdapter);

    NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)hAdapter;

    switch (InterruptType)
    {
        case DXGK_INTERRUPT_DISPLAYONLY_VSYNC:
        case DXGK_INTERRUPT_CRTC_VSYNC:
        {
            LOGI("DXGK_INTERRUPT_CRTC_VSYNC %s\n", Enable ? "enable" : "disable");
            Status = VBoxWddmSlEnableVSyncNotification(pDevExt, Enable);
            if (NT_SUCCESS(Status))
                Status = STATUS_SUCCESS; /* <- sanity */
            else
                LOGE("VSYNC Interrupt control failed Enable(%d), Status(0x%x)\n", Enable, Status);
            break;
        }
        case DXGK_INTERRUPT_DMA_COMPLETED:
        case DXGK_INTERRUPT_DMA_PREEMPTED:
        case DXGK_INTERRUPT_DMA_FAULTED:
            LOGE("Unexpected interrupt type! %d\n", InterruptType);
            break;
        default:
            LOGE("UNSUPPORTED interrupt type! %d\n", InterruptType);
            break;
    }

    LOGI("<== hAdapter(0x%p)\n", hAdapter);

    return Status;
}

NTSTATUS APIENTRY DxgkDdiSetDisplayPrivateDriverFormat(
    CONST HANDLE hAdapter,
    DXGKARG_SETDISPLAYPRIVATEDRIVERFORMAT *pSetDisplayPrivateDriverFormat)
{
    RT_NOREF(hAdapter, pSetDisplayPrivateDriverFormat);
    VBoxGpuDbgBreak();

    LOGI("ENTER==>, hAdapter(0x%p)\n", hAdapter);
    return STATUS_SUCCESS;
}

NTSTATUS APIENTRY DxgkDdiGetScanLine(
    CONST HANDLE hAdapter,
    DXGKARG_GETSCANLINE *pGetScanLine)
{
    RT_NOREF(hAdapter, pGetScanLine);
    LOGI("ENTER==>, hAdapter(0x%p)\n", hAdapter);
    return STATUS_SUCCESS;
}

NTSTATUS APIENTRY DxgkDdiStopCapture(
    CONST HANDLE hAdapter,
    CONST DXGKARG_STOPCAPTURE *pStopCapture)
{
    RT_NOREF(hAdapter, pStopCapture);
    LOGI("ENTER==>, hAdapter(0x%p)\n", hAdapter);
    return STATUS_SUCCESS;
}

extern "C"
NTSTATUS vboxWddmInitFullGraphicsDriver(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    DRIVER_INITIALIZATION_DATA DriverInitializationData = {'\0'};
    VBoxGpuDbgBreak();
    LOGI("Enter ==> \n");

    memset(&DriverInitializationData, 0, sizeof(DriverInitializationData));

    DriverInitializationData.Version = DXGKDDI_INTERFACE_VERSION_WIN8;

    /* Deivce and query interface */
    DriverInitializationData.DxgkDdiAddDevice                           = DxgkDdiAddDevice;
    DriverInitializationData.DxgkDdiRemoveDevice                        = DxgkDdiRemoveDevice;
    DriverInitializationData.DxgkDdiStartDevice                         = DxgkDdiStartDevice;
    DriverInitializationData.DxgkDdiStopDevice                          = DxgkDdiStopDevice;
    DriverInitializationData.DxgkDdiCreateDevice                        = DxgkDdiCreateDevice;
    DriverInitializationData.DxgkDdiDestroyDevice                       = DxgkDdiDestroyDevice;
    DriverInitializationData.DxgkDdiCreateContext                       = DxgkDdiCreateContext;
    DriverInitializationData.DxgkDdiDestroyContext                      = DxgkDdiDestroyContext;
    DriverInitializationData.DxgkDdiResetFromTimeout                    = DxgkDdiResetFromTimeout;
    DriverInitializationData.DxgkDdiRestartFromTimeout                  = DxgkDdiRestartFromTimeout;
    DriverInitializationData.DxgkDdiQueryAdapterInfo                    = DxgkDdiQueryAdapterInfo;
    DriverInitializationData.DxgkDdiSetPowerState                       = DxgkDdiSetPowerState;
    DriverInitializationData.DxgkDdiNotifyAcpiEvent                     = DxgkDdiNotifyAcpiEvent;
    DriverInitializationData.DxgkDdiResetDevice                         = DxgkDdiResetDevice;
    DriverInitializationData.DxgkDdiUnload                              = DxgkDdiUnload;
    DriverInitializationData.DxgkDdiQueryInterface                      = DxgkDdiQueryInterface;
    DriverInitializationData.DxgkDdiQueryDeviceDescriptor               = DxgkDdiQueryDeviceDescriptor;
    DriverInitializationData.DxgkDdiQueryCurrentFence                   = GaDxgkDdiQueryCurrentFence;

    /* VidPn */
    DriverInitializationData.DxgkDdiIsSupportedVidPn                    = DxgkDdiIsSupportedVidPn;
    DriverInitializationData.DxgkDdiRecommendFunctionalVidPn            = DxgkDdiRecommendFunctionalVidPn;
    DriverInitializationData.DxgkDdiEnumVidPnCofuncModality             = DxgkDdiEnumVidPnCofuncModality;
    DriverInitializationData.DxgkDdiSetVidPnSourceAddress               = DxgkDdiSetVidPnSourceAddress;
    DriverInitializationData.DxgkDdiSetVidPnSourceVisibility            = DxgkDdiSetVidPnSourceVisibility;
    DriverInitializationData.DxgkDdiCommitVidPn                         = DxgkDdiCommitVidPn;
    DriverInitializationData.DxgkDdiUpdateActiveVidPnPresentPath        = DxgkDdiUpdateActiveVidPnPresentPath;
    DriverInitializationData.DxgkDdiRecommendMonitorModes               = DxgkDdiRecommendMonitorModes;

    /* Allocation */
    DriverInitializationData.DxgkDdiCreateAllocation                    = DxgkDdiCreateAllocation;
    DriverInitializationData.DxgkDdiDestroyAllocation                   = DxgkDdiDestroyAllocation;
    DriverInitializationData.DxgkDdiDescribeAllocation                  = DxgkDdiDescribeAllocation;
    DriverInitializationData.DxgkDdiGetStandardAllocationDriverData     = DxgkDdiGetStandardAllocationDriverData;
    DriverInitializationData.DxgkDdiOpenAllocation                      = DxgkDdiOpenAllocation;
    DriverInitializationData.DxgkDdiCloseAllocation                     = DxgkDdiCloseAllocation;

    /* interrupt and cmd */
    DriverInitializationData.DxgkDdiInterruptRoutine                    = DxgkDdiInterruptRoutine;
    DriverInitializationData.DxgkDdiDpcRoutine                          = DxgkDdiDpcRoutine;
    DriverInitializationData.DxgkDdiSubmitCommand                       = GaDxgkDdiSubmitCommand;
    DriverInitializationData.DxgkDdiPreemptCommand                      = GaDxgkDdiPreemptCommand;
    DriverInitializationData.DxgkDdiEscape                              = DxgkDdiEscape;

    /* graphics */
    DriverInitializationData.DxgkDdiPatch                               = GaDxgkDdiPatch;
    DriverInitializationData.DxgkDdiRender                              = GaDxgkDdiRender;
    DriverInitializationData.DxgkDdiPresent                             = GaDxgkDdiPresent;

    /* others */
    DriverInitializationData.DxgkDdiQueryChildRelations                 = DxgkDdiQueryChildRelations;
    DriverInitializationData.DxgkDdiQueryChildStatus                    = DxgkDdiQueryChildStatus;
    DriverInitializationData.DxgkDdiBuildPagingBuffer                   = GaDxgkDdiBuildPagingBuffer;
    DriverInitializationData.DxgkDdiSetPointerPosition                  = DxgkDdiSetPointerPosition;
    DriverInitializationData.DxgkDdiSetPointerShape                     = DxgkDdiSetPointerShape;
    DriverInitializationData.DxgkDdiDispatchIoRequest                   = DxgkDdiDispatchIoRequest;
    DriverInitializationData.DxgkDdiSetPalette                          = DxgkDdiSetPalette;
    DriverInitializationData.DxgkDdiControlInterrupt                    = DxgkDdiControlInterrupt;
    DriverInitializationData.DxgkDdiSetDisplayPrivateDriverFormat       = DxgkDdiSetDisplayPrivateDriverFormat;
    DriverInitializationData.DxgkDdiControlEtwLogging                   = DxgkDdiControlEtwLogging;
    DriverInitializationData.DxgkDdiCollectDbgInfo                      = DxgkDdiCollectDbgInfo;
    DriverInitializationData.DxgkDdiGetScanLine                         = DxgkDdiGetScanLine;
    DriverInitializationData.DxgkDdiStopCapture                         = DxgkDdiStopCapture;

    DriverInitializationData.DxgkDdiSystemDisplayEnable = DxgkDdiSystemDisplayEnable;
    DriverInitializationData.DxgkDdiSystemDisplayWrite = DxgkDdiSystemDisplayWrite;
    DriverInitializationData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = DxgkDdiStopDeviceAndReleasePostDisplayOwnership;

    if (DriverInitializationData.Version >= DXGKDDI_INTERFACE_VERSION_WIN7)
    {
        DriverInitializationData.DxgkDdiQueryVidPnHWCapability = DxgkDdiQueryVidPnHWCapability;
    }

    NTSTATUS Status = DxgkInitialize(pDriverObject, pRegistryPath, &DriverInitializationData);
    if (!NT_SUCCESS(Status)) {
        LOGF("DxgkInitialize failed! Status 0x%x\n", Status);
    }

    LOGI("Leave <== \n");

    return Status;
}

extern "C"
NTSTATUS
DriverEntry(
    _In_  DRIVER_OBJECT*  pDriverObject,
    _In_  UNICODE_STRING* pRegistryPath)
{
    NTSTATUS Status = STATUS_SUCCESS;

    PAGED_CODE();
    VBoxGpuDbgBreak();

    UNREFERENCED_PARAMETER(pDriverObject);
    UNREFERENCED_PARAMETER(pRegistryPath);

    g_windowsVersion = VBoxQueryWinVersion(NULL);
    if (g_windowsVersion < WINVERSION_8) {
        LOGE("Unsupported Windows version :%d\n", g_windowsVersion);
        return STATUS_UNSUCCESSFUL;
    }

    g_VBoxDisplayOnly = !qemu_support_3d();
    DbgPrint("g_VBoxDisplayOnly=%d\n", g_VBoxDisplayOnly);
    if (g_VBoxDisplayOnly)
    {
        Status = vboxWddmInitDisplayOnlyDriver(pDriverObject, pRegistryPath);
    }
    else
    {
        Status = vboxWddmInitFullGraphicsDriver(pDriverObject, pRegistryPath);
    }

    if (NT_SUCCESS(Status))
    {
        /* Successfully initialized the driver. */
        return Status;
    }

    LOGE("filed driverentry  Status=%d \n", Status);
    return Status;
}
